TUTORIAL_SYSTEM.md

Sistema de Tutoriais Interativos — Manual Tecnico

Public Markdown Document

Sistema de Tutoriais Interativos — Manual Tecnico

Documento vivo. Sistema de onboarding guiado integrado ao Fastapp Engine com Driver.js. Tutoriais vivem como JSON dentro da entidade App (Estrategia A — Cidadao Pleno do App).

832 palavras24 headingsrepository root

Sistema de Tutoriais Interativos — Manual Tecnico

Documento vivo. Sistema de onboarding guiado integrado ao Fastapp Engine com Driver.js.
Tutoriais vivem como JSON dentro da entidade App (Estrategia A — Cidadao Pleno do App).


Visao Geral

O Sistema de Tutoriais permite criar e gerenciar tutoriais interativos que guiam usuarios pela interface, destacando elementos da UI passo a passo. O sistema e multi-tenant (por App), respeita o modelo de Rules/Permissoes do RBAC, e pode gerar Workflows automaticamente a partir dos passos do tutorial.

graph LR
    subgraph "Admin (Builder)"
        EDITOR["Tutorial Editor<br/>Visual Step Builder"]
        LIST["Lista de Tutoriais<br/>Filtros por App/Rule"]
        ANALYTICS["Analytics<br/>Metricas de engajamento"]
    end

    subgraph "Runtime (Usuario)"
        CHECK["Plugin Check<br/>Verifica disponiveis"]
        PROMPT["Prompt<br/>Popup de convite"]
        RUNNER["Runner<br/>Driver.js execucao"]
    end

    subgraph "Backend"
        API["Tutorial API<br/>CRUD + Status"]
        APPJSON["App JSON<br/>app.tutorials[]"]
        WFJSON["App JSON<br/>app.workflows[]"]
        DB["PostgreSQL<br/>tutorial_user_status"]
    end

    EDITOR -->|"salva"| API
    LIST -->|"lista"| API
    ANALYTICS -->|"consulta"| API
    API --> APPJSON
    API -->|"gera workflow"| WFJSON
    API --> DB

    CHECK -->|"GET available"| API
    CHECK -->|"tutoriais encontrados"| PROMPT
    PROMPT -->|"Iniciar"| RUNNER
    RUNNER -->|"POST status"| DB

    style EDITOR fill:#6366f1,color:#fff
    style RUNNER fill:#059669,color:#fff
    style API fill:#7c3aed,color:#fff
    style APPJSON fill:#0891b2,color:#fff
    style WFJSON fill:#0891b2,color:#fff

Stack & Dependencias

Camada Tecnologia Funcao
Highlight Engine Driver.js 1.x Overlay, steps, navegacao passo-a-passo
UI Components PrimeVue (AutoComplete, Select, Checkbox, RadioButton) Formularios do editor
Drag & Drop vuedraggable Reordenacao de passos no editor
State Pinia (tutorial.ts) Estado global de tutoriais
Composable useTutorials.ts Integracao Driver.js + logica de negocio
API Nitro (server/api/tutorial/) CRUD + status + analytics
Storage App JSON (app.tutorials) Definicoes de tutorial dentro da entidade App
Workflow Link server/utils/tutorial-workflow.ts Conversao tutorial → workflow
User Tracking Prisma (tutorial_user_status) Status por usuario (progress/dismiss)
DB PostgreSQL Persistencia

1. Modelo de Dados (DNA)

Arquitetura: Tutorial como JSON da App

Tutoriais nao possuem tabela propria. Sao armazenados como JSON dentro da coluna app.tutorials:

App (tabela) → tutorials: Json (TutorialDefinition[])

A unica tabela relacional e tutorial_user_status, que rastreia progresso por usuario:

model tutorial_user_status {
  id           Int       @id @default(autoincrement())
  user_id      Int
  app_id       Int
  tutorial_id  String                                    // ID string do tutorial no JSON
  rule         String?
  status       String    @default("not_started")         // not_started | in_progress | completed
  dismissed    Boolean   @default(false)                  // "Nao mostrar novamente"
  dismissed_at DateTime? @db.Timestamptz(6)
  completed_at DateTime? @db.Timestamptz(6)
  created_at   DateTime  @default(now()) @db.Timestamptz(6)
  updated_at   DateTime? @default(now()) @db.Timestamptz(6)

  @@unique([user_id, tutorial_id])
  @@index([user_id])
  @@index([app_id])
  @@index([tutorial_id])
}

Interface: TutorialDefinition (JSON dentro de app.tutorials)

interface TutorialStepDefinition {
  id: string;                          // step_<timestamp>_<random>
  order: number;                       // Ordem de execucao (1-based)
  elementSelector: string;             // Seletor CSS do elemento
  title: string;                       // Titulo do popover
  description: string;                 // Instrucao do passo
  highlightType: 'element' | 'region';
}

interface TutorialDefinition {
  id: string;                          // tut_<timestamp>_<random>
  name: string;
  description?: string;
  rule?: string | null;                // null = todos os usuarios
  is_active: boolean;
  steps: TutorialStepDefinition[];
  generate_workflow?: boolean;         // Gerar workflow automaticamente
  workflow_id?: string;                // ID do workflow vinculado
}

Relacionamento entre Entidades

erDiagram
    APP ||--o{ TUTORIAL_JSON : "tutorials JSON[]"
    APP ||--o{ WORKFLOW_JSON : "workflows JSON[]"
    TUTORIAL_JSON ||--o| WORKFLOW_JSON : "generate_workflow"
    TUTORIAL_JSON ||--o{ TUTORIAL_USER_STATUS : "tracks"
    USER ||--o{ TUTORIAL_USER_STATUS : "has status"

    APP {
        int id PK
        string slug
        json tutorials "TutorialDefinition[]"
        json workflows "WorkflowDefinition[]"
    }
    TUTORIAL_JSON {
        string id "tut_xxx"
        string name
        string rule "nullable"
        boolean is_active
        json steps "TutorialStepDefinition[]"
        boolean generate_workflow
        string workflow_id "nullable"
    }
    WORKFLOW_JSON {
        string id "wf_from_tut_xxx"
        string label
        string source_type "manual | tutorial"
        string source_id "tutorial ID"
        json steps "WorkflowStep[]"
    }
    TUTORIAL_USER_STATUS {
        int id PK
        int user_id
        string tutorial_id "string FK"
        string status "not_started|in_progress|completed"
        boolean dismissed
    }

2. Integracao Tutorial x Workflow

Conceito

Quando generate_workflow = true, o sistema converte automaticamente os passos do tutorial em steps de um Workflow visual. A relacao e bidirecional via IDs:

TutorialDefinition.workflow_id  ←→  WorkflowDefinition.source_id

Conversao de Steps

TutorialStep                    →  WorkflowStep
───────────────────────────────────────────────────
id: "step_1"                    →  id: "wf_tut_xxx_step_0"
title: "Preencha o formulario"  →  label: "Preencha o formulario"
elementSelector: "#form-lead"   →  object: "lead" (extraido do selector)
order: 0                        →  position: { x: 0*290, y: 180 }
(proximo step)                  →  next_step: "wf_tut_xxx_step_1"
highlightType: "element"        →  icon: "pi pi-compass"

Ciclo de Vida

Acao no Tutorial Efeito no Workflow
Criar com generate_workflow: true Cria workflow wf_from_tut_xxx
Editar steps/nome Atualiza o workflow vinculado
Desativar generate_workflow Remove o workflow vinculado
Deletar tutorial Remove o workflow vinculado

Utilitario: server/utils/tutorial-workflow.ts

Funcao Descricao
tutorialStepsToWorkflowSteps() Converte TutorialStep[] em WorkflowStep[]
generateWorkflowFromTutorial() Gera WorkflowDefinition completa
upsertTutorialWorkflow() Insere ou atualiza workflow no array
removeTutorialWorkflow() Remove workflow gerado do array

3. API Endpoints

Todos os endpoints estao em server/api/tutorial/.

CRUD

Rota Metodo Descricao
/api/tutorial GET Lista tutoriais (filtros: app_id, rule, active)
/api/tutorial POST Cria tutorial + workflow opcional (generate_workflow)
/api/tutorial/:id GET Busca tutorial por ID (requer app_id query)
/api/tutorial/:id PATCH Atualiza tutorial + sincroniza workflow
/api/tutorial/:id DELETE Exclui tutorial + workflow vinculado + statuses

Status & Disponibilidade

Rota Metodo Descricao
/api/tutorial/available GET Tutoriais disponiveis para o usuario (exclui dismissed)
/api/tutorial/status POST Atualiza status: start, complete, dismiss
/api/tutorial/analytics GET Metricas: iniciaram, completaram, dispensaram

Suporte ao Editor

Rota Metodo Descricao
/api/tutorial/roles GET Roles disponiveis (padrao + existentes nos tutoriais)
/api/tutorial/selectors GET Seletores CSS predefinidos da plataforma

Logica de Disponibilidade (available.get.ts)

graph TD
    REQ["GET /api/tutorial/available<br/>?user_id=X&app_id=Y&rules=a,b"]
    APPJSON["Ler app.tutorials JSON<br/>filtrar por is_active + rule"]
    STATUS["Buscar statuses do user<br/>na tabela tutorial_user_status"]
    FILTER["Filtrar: remover apenas<br/>dismissed = true"]
    RESULT["Retornar tutoriais disponiveis"]

    REQ --> APPJSON --> STATUS --> FILTER --> RESULT

    style REQ fill:#2563eb,color:#fff
    style APPJSON fill:#0891b2,color:#fff
    style FILTER fill:#f59e0b,color:#000
    style RESULT fill:#059669,color:#fff

Regra importante: Tutoriais completados continuam aparecendo. Apenas dismissed = true (checkbox "Nao mostrar novamente") impede que o tutorial reapareca.


4. Arquitetura de Componentes

Estrutura de Diretorios

app/
├── components/
│   └── tutorial/
│       ├── TutorialEditor.vue              # Visual Step Builder (admin)
│       ├── TutorialInitialPrompt.vue       # Popup de convite (runtime)
│       └── TutorialRunner.vue              # Watcher que dispara Driver.js
├── composables/
│   └── useTutorials.ts                     # Integracao Driver.js + logica
├── stores/
│   └── tutorial.ts                         # Pinia store (status + estado UI)
├── plugins/
│   └── tutorial-check.client.ts            # Verifica tutoriais apos login
├── pages/
│   └── fastapp/
│       └── tutorials/
│           ├── index.vue                   # Lista de tutoriais (admin)
│           ├── new.vue                     # Criar novo tutorial
│           └── [id].vue                    # Editar tutorial
server/
├── api/
│   └── tutorial/
│       ├── index.get.ts                    # Lista (filtros)
│       ├── index.post.ts                   # Criar + gerar workflow
│       ├── [id].get.ts                     # Buscar por ID
│       ├── [id].patch.ts                   # Atualizar + sync workflow
│       ├── [id].delete.ts                  # Excluir + cleanup workflow
│       ├── available.get.ts                # Disponiveis para o usuario
│       ├── status.post.ts                  # Atualizar status
│       ├── analytics.get.ts                # Metricas
│       ├── roles.get.ts                    # Roles para autocomplete
│       └── selectors.get.ts                # Seletores CSS predefinidos
└── utils/
    └── tutorial-workflow.ts                # Conversao tutorial → workflow

5. Fluxo de Runtime

Fluxo Completo

sequenceDiagram
    participant User as Usuario
    participant Plugin as tutorial-check.client.ts
    participant Store as TutorialStore
    participant API as /api/tutorial
    participant Prompt as TutorialInitialPrompt
    participant Composable as useTutorials
    participant Driver as Driver.js

    Note over User: Login → navega para /app-slug/...
    Plugin->>API: GET /api/tutorial/available?user_id&app_id&rules
    API-->>Plugin: Tutorial[] (nao-dismissed, lidos do App JSON)
    Plugin->>Store: availableTutorials = [T1, T2]
    Store->>Prompt: showPrompt = true, currentTutorial = T1

    Note over Prompt: Popup aparece (bottom-right)
    Prompt-->>User: "Tutorial 1 de 2: Tour pelo Dashboard"

    alt Clica "Iniciar"
        User->>Prompt: click Iniciar
        Prompt->>Composable: runTutorial(T1)
        Composable->>API: POST /api/tutorial/status {action: "start"}
        Composable->>Driver: driver.drive()
        Driver-->>User: Overlay + Steps

        alt Completa todos os passos
            Driver->>Composable: onDestroyStarted (hasNextStep = false)
            Composable->>API: POST /api/tutorial/status {action: "complete"}
            Composable->>Store: remove T1 de availableTutorials
            Store->>Prompt: showNextAvailable() → T2
        else Pula o tutorial
            Driver->>Composable: onDestroyStarted (hasNextStep = true)
            Composable->>Store: stopRunning → showNextAvailable
        end

    else Marca "Nao mostrar novamente"
        User->>Prompt: checkbox + fecha
        Prompt->>Composable: dismissTutorial(T1)
        Composable->>API: POST /api/tutorial/status {action: "dismiss"}
        Note over API: dismissed=true → tutorial nunca mais aparece
    end

6. Painel Administrativo

Acessivel em

/fastapp/tutorials — Registrado como Page no JSON da App fastapp.

Lista (/fastapp/tutorials)

  • Filtros por App (Select) e Rule (InputText)
  • Toggle de Ativo/Inativo por tutorial
  • Exclusao com confirmacao (ConfirmDialog)
  • Badge "Workflow" clicavel nos tutoriais que possuem workflow vinculado
  • Painel de Analytics expansivel com metricas por tutorial

Editor (/fastapp/tutorials/new e /fastapp/tutorials/:id)

Campo Tipo Descricao
Nome InputText Nome do tutorial (obrigatorio)
App Select Aplicacao destino (obrigatorio)
Publico-alvo RadioButton "Todos os usuarios" ou "Rule especifica"
Rule AutoComplete (dropdown) Roles disponiveis
Ativo ToggleSwitch Liga/desliga exibicao
Gerar Workflow ToggleSwitch Gera workflow automaticamente no Workflow Studio
Descricao Textarea Descricao do tutorial

Quando Gerar Workflow esta ativo e o tutorial tem steps:

  • Um workflow wf_from_tut_xxx e criado/atualizado em app.workflows
  • Botao "Ver Workflow" aparece no header do editor
  • Na lista, um badge cyan clicavel leva ao Workflow Studio

7. Integracao com Driver.js

Configuracao

// useTutorials.ts → createDriver()
driver({
    showProgress: true,
    animate: true,
    overlayOpacity: 0.4,
    allowClose: true,
    stagePadding: 8,
    stageRadius: 8,
    popoverClass: 'tutorial-popover',
    nextBtnText: 'Proximo',
    prevBtnText: 'Anterior',
    doneBtnText: 'Concluir',
    progressText: '{{current}} de {{total}}',
    onDestroyStarted: () => { /* salvar progresso */ }
});

8. Analytics

Endpoint: GET /api/tutorial/analytics

Retorna metricas agregadas por tutorial (lidas do App JSON + cruzadas com tutorial_user_status):

[
  {
    "id": "tut_1711234567_abc",
    "name": "Tour pelo Dashboard",
    "app_id": 5,
    "is_active": true,
    "total_users": 45,
    "started": 12,
    "completed": 30,
    "dismissed": 3
  }
]

9. Como Criar um Tutorial

  1. Navegue para /fastapp/tutorials"Novo Tutorial"
  2. Preencha Nome, App, Publico-alvo, Descricao
  3. Ative "Gerar Workflow" se quiser criar um workflow visual automatico
  4. Adicione Passos: titulo, seletor CSS (dropdown ou livre), descricao
  5. Use o botao de validacao para testar se o seletor existe na pagina
  6. Clique "Preview" para testar no Driver.js
  7. Clique "Criar" — tutorial fica ativo imediatamente

Se "Gerar Workflow" estiver ativo, o workflow aparece no Workflow Studio (/fastapp/workflows).


10. Pontos Importantes

Armazenamento

  • Tutoriais vivem dentro do JSON da App (app.tutorials)
  • Nao possuem tabela propria — seguem o mesmo padrao de objects, modules, pages
  • Mutacoes via PATCH /api/app/:id (FADAP protocol)

Tutorial x Workflow

  • Vinculo via tutorial.workflow_idworkflow.source_id
  • Workflow gerado e atualizado automaticamente quando tutorial e editado
  • Workflow gerado e removido quando tutorial e deletado ou generate_workflow desativado
  • Workflows manuais (criados no Studio) nao sao afetados

Multi-Tenancy

  • Cada tutorial pertence a uma App (dentro do JSON)
  • Status e por usuario x tutorial (unique constraint na tabela)
  • Filtro de app_id na query garante isolamento

Regras de Rule

  • rule: null → tutorial para todos os usuarios da App
  • rule: "admin" → apenas usuarios com essa ability/role
  • Um usuario pode ter multiplos tutoriais (genericos + especificos)

Persistencia de Preferencia

  • "Nao mostrar novamente" seta dismissed = true — irreversivel via UI
  • Completar um tutorial nao impede reexibicao — apenas registra analytics
  • "Nao agora" apenas fecha o popup — tutorial reaparece na proxima sessao

Data: 13/04/2026
Branch: feat/panel-tutorial
Status: Implementado — CRUD + Runtime + Analytics + Admin Panel + Workflow Integration