{"id":"repo/tutorial-system","source":"repo","sourceLabel":"repository root","sourcePath":"TUTORIAL_SYSTEM.md","routePath":"/fastapp/public-docs/page/repo/tutorial-system","rawPath":"/fastapp/public-docs/raw/repo/tutorial-system","slug":["repo","tutorial-system"],"slugPath":"repo/tutorial-system","title":"Sistema de Tutoriais Interativos — Manual Tecnico","description":"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).","headings":["Sistema de Tutoriais Interativos — Manual Tecnico","Visao Geral","Stack & Dependencias","1. Modelo de Dados (DNA)","Arquitetura: Tutorial como JSON da App","Interface: TutorialDefinition (JSON dentro de app.tutorials)","Relacionamento entre Entidades","2. Integracao Tutorial x Workflow","Conceito","Conversao de Steps","Ciclo de Vida","Utilitario: server/utils/tutorial workflow.ts","3. API Endpoints","CRUD","Status & Disponibilidade","Suporte ao Editor","Logica de Disponibilidade (available.get.ts)","4. Arquitetura de Componentes","Estrutura de Diretorios","5. Fluxo de Runtime","Fluxo Completo","6. Painel Administrativo","Acessivel em","Lista (/fastapp/tutorials)"],"wordCount":832,"markdown":"# Sistema de Tutoriais Interativos — Manual Tecnico\r\n\r\n> Documento vivo. Sistema de onboarding guiado integrado ao Fastapp Engine com Driver.js.\r\n> Tutoriais vivem como JSON dentro da entidade App (Estrategia A — Cidadao Pleno do App).\r\n\r\n---\r\n\r\n## Visao Geral\r\n\r\nO **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.\r\n\r\n```mermaid\r\ngraph LR\r\n    subgraph \"Admin (Builder)\"\r\n        EDITOR[\"Tutorial Editor<br/>Visual Step Builder\"]\r\n        LIST[\"Lista de Tutoriais<br/>Filtros por App/Rule\"]\r\n        ANALYTICS[\"Analytics<br/>Metricas de engajamento\"]\r\n    end\r\n\r\n    subgraph \"Runtime (Usuario)\"\r\n        CHECK[\"Plugin Check<br/>Verifica disponiveis\"]\r\n        PROMPT[\"Prompt<br/>Popup de convite\"]\r\n        RUNNER[\"Runner<br/>Driver.js execucao\"]\r\n    end\r\n\r\n    subgraph \"Backend\"\r\n        API[\"Tutorial API<br/>CRUD + Status\"]\r\n        APPJSON[\"App JSON<br/>app.tutorials[]\"]\r\n        WFJSON[\"App JSON<br/>app.workflows[]\"]\r\n        DB[\"PostgreSQL<br/>tutorial_user_status\"]\r\n    end\r\n\r\n    EDITOR -->|\"salva\"| API\r\n    LIST -->|\"lista\"| API\r\n    ANALYTICS -->|\"consulta\"| API\r\n    API --> APPJSON\r\n    API -->|\"gera workflow\"| WFJSON\r\n    API --> DB\r\n\r\n    CHECK -->|\"GET available\"| API\r\n    CHECK -->|\"tutoriais encontrados\"| PROMPT\r\n    PROMPT -->|\"Iniciar\"| RUNNER\r\n    RUNNER -->|\"POST status\"| DB\r\n\r\n    style EDITOR fill:#6366f1,color:#fff\r\n    style RUNNER fill:#059669,color:#fff\r\n    style API fill:#7c3aed,color:#fff\r\n    style APPJSON fill:#0891b2,color:#fff\r\n    style WFJSON fill:#0891b2,color:#fff\r\n```\r\n\r\n---\r\n\r\n## Stack & Dependencias\r\n\r\n| Camada | Tecnologia | Funcao |\r\n|--------|-----------|--------|\r\n| Highlight Engine | Driver.js 1.x | Overlay, steps, navegacao passo-a-passo |\r\n| UI Components | PrimeVue (AutoComplete, Select, Checkbox, RadioButton) | Formularios do editor |\r\n| Drag & Drop | vuedraggable | Reordenacao de passos no editor |\r\n| State | Pinia (`tutorial.ts`) | Estado global de tutoriais |\r\n| Composable | `useTutorials.ts` | Integracao Driver.js + logica de negocio |\r\n| API | Nitro (server/api/tutorial/) | CRUD + status + analytics |\r\n| Storage | App JSON (`app.tutorials`) | Definicoes de tutorial dentro da entidade App |\r\n| Workflow Link | `server/utils/tutorial-workflow.ts` | Conversao tutorial → workflow |\r\n| User Tracking | Prisma (`tutorial_user_status`) | Status por usuario (progress/dismiss) |\r\n| DB | PostgreSQL | Persistencia |\r\n\r\n---\r\n\r\n## 1. Modelo de Dados (DNA)\r\n\r\n### Arquitetura: Tutorial como JSON da App\r\n\r\nTutoriais **nao** possuem tabela propria. Sao armazenados como JSON dentro da coluna `app.tutorials`:\r\n\r\n```\r\nApp (tabela) → tutorials: Json (TutorialDefinition[])\r\n```\r\n\r\nA unica tabela relacional e `tutorial_user_status`, que rastreia progresso por usuario:\r\n\r\n```prisma\r\nmodel tutorial_user_status {\r\n  id           Int       @id @default(autoincrement())\r\n  user_id      Int\r\n  app_id       Int\r\n  tutorial_id  String                                    // ID string do tutorial no JSON\r\n  rule         String?\r\n  status       String    @default(\"not_started\")         // not_started | in_progress | completed\r\n  dismissed    Boolean   @default(false)                  // \"Nao mostrar novamente\"\r\n  dismissed_at DateTime? @db.Timestamptz(6)\r\n  completed_at DateTime? @db.Timestamptz(6)\r\n  created_at   DateTime  @default(now()) @db.Timestamptz(6)\r\n  updated_at   DateTime? @default(now()) @db.Timestamptz(6)\r\n\r\n  @@unique([user_id, tutorial_id])\r\n  @@index([user_id])\r\n  @@index([app_id])\r\n  @@index([tutorial_id])\r\n}\r\n```\r\n\r\n### Interface: `TutorialDefinition` (JSON dentro de `app.tutorials`)\r\n\r\n```typescript\r\ninterface TutorialStepDefinition {\r\n  id: string;                          // step_<timestamp>_<random>\r\n  order: number;                       // Ordem de execucao (1-based)\r\n  elementSelector: string;             // Seletor CSS do elemento\r\n  title: string;                       // Titulo do popover\r\n  description: string;                 // Instrucao do passo\r\n  highlightType: 'element' | 'region';\r\n}\r\n\r\ninterface TutorialDefinition {\r\n  id: string;                          // tut_<timestamp>_<random>\r\n  name: string;\r\n  description?: string;\r\n  rule?: string | null;                // null = todos os usuarios\r\n  is_active: boolean;\r\n  steps: TutorialStepDefinition[];\r\n  generate_workflow?: boolean;         // Gerar workflow automaticamente\r\n  workflow_id?: string;                // ID do workflow vinculado\r\n}\r\n```\r\n\r\n### Relacionamento entre Entidades\r\n\r\n```mermaid\r\nerDiagram\r\n    APP ||--o{ TUTORIAL_JSON : \"tutorials JSON[]\"\r\n    APP ||--o{ WORKFLOW_JSON : \"workflows JSON[]\"\r\n    TUTORIAL_JSON ||--o| WORKFLOW_JSON : \"generate_workflow\"\r\n    TUTORIAL_JSON ||--o{ TUTORIAL_USER_STATUS : \"tracks\"\r\n    USER ||--o{ TUTORIAL_USER_STATUS : \"has status\"\r\n\r\n    APP {\r\n        int id PK\r\n        string slug\r\n        json tutorials \"TutorialDefinition[]\"\r\n        json workflows \"WorkflowDefinition[]\"\r\n    }\r\n    TUTORIAL_JSON {\r\n        string id \"tut_xxx\"\r\n        string name\r\n        string rule \"nullable\"\r\n        boolean is_active\r\n        json steps \"TutorialStepDefinition[]\"\r\n        boolean generate_workflow\r\n        string workflow_id \"nullable\"\r\n    }\r\n    WORKFLOW_JSON {\r\n        string id \"wf_from_tut_xxx\"\r\n        string label\r\n        string source_type \"manual | tutorial\"\r\n        string source_id \"tutorial ID\"\r\n        json steps \"WorkflowStep[]\"\r\n    }\r\n    TUTORIAL_USER_STATUS {\r\n        int id PK\r\n        int user_id\r\n        string tutorial_id \"string FK\"\r\n        string status \"not_started|in_progress|completed\"\r\n        boolean dismissed\r\n    }\r\n```\r\n\r\n---\r\n\r\n## 2. Integracao Tutorial x Workflow\r\n\r\n### Conceito\r\n\r\nQuando `generate_workflow = true`, o sistema converte automaticamente os passos do tutorial em steps de um Workflow visual. A relacao e bidirecional via IDs:\r\n\r\n```\r\nTutorialDefinition.workflow_id  ←→  WorkflowDefinition.source_id\r\n```\r\n\r\n### Conversao de Steps\r\n\r\n```\r\nTutorialStep                    →  WorkflowStep\r\n───────────────────────────────────────────────────\r\nid: \"step_1\"                    →  id: \"wf_tut_xxx_step_0\"\r\ntitle: \"Preencha o formulario\"  →  label: \"Preencha o formulario\"\r\nelementSelector: \"#form-lead\"   →  object: \"lead\" (extraido do selector)\r\norder: 0                        →  position: { x: 0*290, y: 180 }\r\n(proximo step)                  →  next_step: \"wf_tut_xxx_step_1\"\r\nhighlightType: \"element\"        →  icon: \"pi pi-compass\"\r\n```\r\n\r\n### Ciclo de Vida\r\n\r\n| Acao no Tutorial | Efeito no Workflow |\r\n|------------------|--------------------|\r\n| Criar com `generate_workflow: true` | Cria workflow `wf_from_tut_xxx` |\r\n| Editar steps/nome | Atualiza o workflow vinculado |\r\n| Desativar `generate_workflow` | Remove o workflow vinculado |\r\n| Deletar tutorial | Remove o workflow vinculado |\r\n\r\n### Utilitario: `server/utils/tutorial-workflow.ts`\r\n\r\n| Funcao | Descricao |\r\n|--------|-----------|\r\n| `tutorialStepsToWorkflowSteps()` | Converte TutorialStep[] em WorkflowStep[] |\r\n| `generateWorkflowFromTutorial()` | Gera WorkflowDefinition completa |\r\n| `upsertTutorialWorkflow()` | Insere ou atualiza workflow no array |\r\n| `removeTutorialWorkflow()` | Remove workflow gerado do array |\r\n\r\n---\r\n\r\n## 3. API Endpoints\r\n\r\nTodos os endpoints estao em `server/api/tutorial/`.\r\n\r\n### CRUD\r\n\r\n| Rota | Metodo | Descricao |\r\n|------|--------|-----------|\r\n| `/api/tutorial` | GET | Lista tutoriais (filtros: `app_id`, `rule`, `active`) |\r\n| `/api/tutorial` | POST | Cria tutorial + workflow opcional (`generate_workflow`) |\r\n| `/api/tutorial/:id` | GET | Busca tutorial por ID (requer `app_id` query) |\r\n| `/api/tutorial/:id` | PATCH | Atualiza tutorial + sincroniza workflow |\r\n| `/api/tutorial/:id` | DELETE | Exclui tutorial + workflow vinculado + statuses |\r\n\r\n### Status & Disponibilidade\r\n\r\n| Rota | Metodo | Descricao |\r\n|------|--------|-----------|\r\n| `/api/tutorial/available` | GET | Tutoriais disponiveis para o usuario (exclui dismissed) |\r\n| `/api/tutorial/status` | POST | Atualiza status: `start`, `complete`, `dismiss` |\r\n| `/api/tutorial/analytics` | GET | Metricas: iniciaram, completaram, dispensaram |\r\n\r\n### Suporte ao Editor\r\n\r\n| Rota | Metodo | Descricao |\r\n|------|--------|-----------|\r\n| `/api/tutorial/roles` | GET | Roles disponiveis (padrao + existentes nos tutoriais) |\r\n| `/api/tutorial/selectors` | GET | Seletores CSS predefinidos da plataforma |\r\n\r\n### Logica de Disponibilidade (`available.get.ts`)\r\n\r\n```mermaid\r\ngraph TD\r\n    REQ[\"GET /api/tutorial/available<br/>?user_id=X&app_id=Y&rules=a,b\"]\r\n    APPJSON[\"Ler app.tutorials JSON<br/>filtrar por is_active + rule\"]\r\n    STATUS[\"Buscar statuses do user<br/>na tabela tutorial_user_status\"]\r\n    FILTER[\"Filtrar: remover apenas<br/>dismissed = true\"]\r\n    RESULT[\"Retornar tutoriais disponiveis\"]\r\n\r\n    REQ --> APPJSON --> STATUS --> FILTER --> RESULT\r\n\r\n    style REQ fill:#2563eb,color:#fff\r\n    style APPJSON fill:#0891b2,color:#fff\r\n    style FILTER fill:#f59e0b,color:#000\r\n    style RESULT fill:#059669,color:#fff\r\n```\r\n\r\n**Regra importante**: Tutoriais **completados** continuam aparecendo. Apenas `dismissed = true` (checkbox \"Nao mostrar novamente\") impede que o tutorial reapareca.\r\n\r\n---\r\n\r\n## 4. Arquitetura de Componentes\r\n\r\n### Estrutura de Diretorios\r\n\r\n```\r\napp/\r\n├── components/\r\n│   └── tutorial/\r\n│       ├── TutorialEditor.vue              # Visual Step Builder (admin)\r\n│       ├── TutorialInitialPrompt.vue       # Popup de convite (runtime)\r\n│       └── TutorialRunner.vue              # Watcher que dispara Driver.js\r\n├── composables/\r\n│   └── useTutorials.ts                     # Integracao Driver.js + logica\r\n├── stores/\r\n│   └── tutorial.ts                         # Pinia store (status + estado UI)\r\n├── plugins/\r\n│   └── tutorial-check.client.ts            # Verifica tutoriais apos login\r\n├── pages/\r\n│   └── fastapp/\r\n│       └── tutorials/\r\n│           ├── index.vue                   # Lista de tutoriais (admin)\r\n│           ├── new.vue                     # Criar novo tutorial\r\n│           └── [id].vue                    # Editar tutorial\r\nserver/\r\n├── api/\r\n│   └── tutorial/\r\n│       ├── index.get.ts                    # Lista (filtros)\r\n│       ├── index.post.ts                   # Criar + gerar workflow\r\n│       ├── [id].get.ts                     # Buscar por ID\r\n│       ├── [id].patch.ts                   # Atualizar + sync workflow\r\n│       ├── [id].delete.ts                  # Excluir + cleanup workflow\r\n│       ├── available.get.ts                # Disponiveis para o usuario\r\n│       ├── status.post.ts                  # Atualizar status\r\n│       ├── analytics.get.ts                # Metricas\r\n│       ├── roles.get.ts                    # Roles para autocomplete\r\n│       └── selectors.get.ts                # Seletores CSS predefinidos\r\n└── utils/\r\n    └── tutorial-workflow.ts                # Conversao tutorial → workflow\r\n```\r\n\r\n---\r\n\r\n## 5. Fluxo de Runtime\r\n\r\n### Fluxo Completo\r\n\r\n```mermaid\r\nsequenceDiagram\r\n    participant User as Usuario\r\n    participant Plugin as tutorial-check.client.ts\r\n    participant Store as TutorialStore\r\n    participant API as /api/tutorial\r\n    participant Prompt as TutorialInitialPrompt\r\n    participant Composable as useTutorials\r\n    participant Driver as Driver.js\r\n\r\n    Note over User: Login → navega para /app-slug/...\r\n    Plugin->>API: GET /api/tutorial/available?user_id&app_id&rules\r\n    API-->>Plugin: Tutorial[] (nao-dismissed, lidos do App JSON)\r\n    Plugin->>Store: availableTutorials = [T1, T2]\r\n    Store->>Prompt: showPrompt = true, currentTutorial = T1\r\n\r\n    Note over Prompt: Popup aparece (bottom-right)\r\n    Prompt-->>User: \"Tutorial 1 de 2: Tour pelo Dashboard\"\r\n\r\n    alt Clica \"Iniciar\"\r\n        User->>Prompt: click Iniciar\r\n        Prompt->>Composable: runTutorial(T1)\r\n        Composable->>API: POST /api/tutorial/status {action: \"start\"}\r\n        Composable->>Driver: driver.drive()\r\n        Driver-->>User: Overlay + Steps\r\n\r\n        alt Completa todos os passos\r\n            Driver->>Composable: onDestroyStarted (hasNextStep = false)\r\n            Composable->>API: POST /api/tutorial/status {action: \"complete\"}\r\n            Composable->>Store: remove T1 de availableTutorials\r\n            Store->>Prompt: showNextAvailable() → T2\r\n        else Pula o tutorial\r\n            Driver->>Composable: onDestroyStarted (hasNextStep = true)\r\n            Composable->>Store: stopRunning → showNextAvailable\r\n        end\r\n\r\n    else Marca \"Nao mostrar novamente\"\r\n        User->>Prompt: checkbox + fecha\r\n        Prompt->>Composable: dismissTutorial(T1)\r\n        Composable->>API: POST /api/tutorial/status {action: \"dismiss\"}\r\n        Note over API: dismissed=true → tutorial nunca mais aparece\r\n    end\r\n```\r\n\r\n---\r\n\r\n## 6. Painel Administrativo\r\n\r\n### Acessivel em\r\n\r\n`/fastapp/tutorials` — Registrado como Page no JSON da App fastapp.\r\n\r\n### Lista (`/fastapp/tutorials`)\r\n- Filtros por **App** (Select) e **Rule** (InputText)\r\n- Toggle de **Ativo/Inativo** por tutorial\r\n- **Exclusao** com confirmacao (ConfirmDialog)\r\n- **Badge \"Workflow\"** clicavel nos tutoriais que possuem workflow vinculado\r\n- **Painel de Analytics** expansivel com metricas por tutorial\r\n\r\n### Editor (`/fastapp/tutorials/new` e `/fastapp/tutorials/:id`)\r\n\r\n| Campo | Tipo | Descricao |\r\n|-------|------|-----------|\r\n| Nome | InputText | Nome do tutorial (obrigatorio) |\r\n| App | Select | Aplicacao destino (obrigatorio) |\r\n| Publico-alvo | RadioButton | \"Todos os usuarios\" ou \"Rule especifica\" |\r\n| Rule | AutoComplete (dropdown) | Roles disponiveis |\r\n| Ativo | ToggleSwitch | Liga/desliga exibicao |\r\n| **Gerar Workflow** | ToggleSwitch | Gera workflow automaticamente no Workflow Studio |\r\n| Descricao | Textarea | Descricao do tutorial |\r\n\r\nQuando **Gerar Workflow** esta ativo e o tutorial tem steps:\r\n- Um workflow `wf_from_tut_xxx` e criado/atualizado em `app.workflows`\r\n- Botao **\"Ver Workflow\"** aparece no header do editor\r\n- Na lista, um badge cyan clicavel leva ao Workflow Studio\r\n\r\n---\r\n\r\n## 7. Integracao com Driver.js\r\n\r\n### Configuracao\r\n\r\n```typescript\r\n// useTutorials.ts → createDriver()\r\ndriver({\r\n    showProgress: true,\r\n    animate: true,\r\n    overlayOpacity: 0.4,\r\n    allowClose: true,\r\n    stagePadding: 8,\r\n    stageRadius: 8,\r\n    popoverClass: 'tutorial-popover',\r\n    nextBtnText: 'Proximo',\r\n    prevBtnText: 'Anterior',\r\n    doneBtnText: 'Concluir',\r\n    progressText: '{{current}} de {{total}}',\r\n    onDestroyStarted: () => { /* salvar progresso */ }\r\n});\r\n```\r\n\r\n---\r\n\r\n## 8. Analytics\r\n\r\n### Endpoint: `GET /api/tutorial/analytics`\r\n\r\nRetorna metricas agregadas por tutorial (lidas do App JSON + cruzadas com `tutorial_user_status`):\r\n\r\n```json\r\n[\r\n  {\r\n    \"id\": \"tut_1711234567_abc\",\r\n    \"name\": \"Tour pelo Dashboard\",\r\n    \"app_id\": 5,\r\n    \"is_active\": true,\r\n    \"total_users\": 45,\r\n    \"started\": 12,\r\n    \"completed\": 30,\r\n    \"dismissed\": 3\r\n  }\r\n]\r\n```\r\n\r\n---\r\n\r\n## 9. Como Criar um Tutorial\r\n\r\n1. Navegue para `/fastapp/tutorials` → **\"Novo Tutorial\"**\r\n2. Preencha **Nome**, **App**, **Publico-alvo**, **Descricao**\r\n3. Ative **\"Gerar Workflow\"** se quiser criar um workflow visual automatico\r\n4. Adicione **Passos**: titulo, seletor CSS (dropdown ou livre), descricao\r\n5. Use o botao de validacao para testar se o seletor existe na pagina\r\n6. Clique **\"Preview\"** para testar no Driver.js\r\n7. Clique **\"Criar\"** — tutorial fica ativo imediatamente\r\n\r\nSe \"Gerar Workflow\" estiver ativo, o workflow aparece no Workflow Studio (`/fastapp/workflows`).\r\n\r\n---\r\n\r\n## 10. Pontos Importantes\r\n\r\n### Armazenamento\r\n- Tutoriais vivem **dentro do JSON da App** (`app.tutorials`)\r\n- Nao possuem tabela propria — seguem o mesmo padrao de `objects`, `modules`, `pages`\r\n- Mutacoes via PATCH `/api/app/:id` (FADAP protocol)\r\n\r\n### Tutorial x Workflow\r\n- Vinculo via `tutorial.workflow_id` ↔ `workflow.source_id`\r\n- Workflow gerado e atualizado automaticamente quando tutorial e editado\r\n- Workflow gerado e removido quando tutorial e deletado ou `generate_workflow` desativado\r\n- Workflows manuais (criados no Studio) nao sao afetados\r\n\r\n### Multi-Tenancy\r\n- Cada tutorial pertence a uma **App** (dentro do JSON)\r\n- Status e por **usuario x tutorial** (unique constraint na tabela)\r\n- Filtro de `app_id` na query garante isolamento\r\n\r\n### Regras de Rule\r\n- `rule: null` → tutorial para **todos** os usuarios da App\r\n- `rule: \"admin\"` → apenas usuarios com essa ability/role\r\n- Um usuario pode ter **multiplos tutoriais** (genericos + especificos)\r\n\r\n### Persistencia de Preferencia\r\n- **\"Nao mostrar novamente\"** seta `dismissed = true` — irreversivel via UI\r\n- **Completar** um tutorial **nao** impede reexibicao — apenas registra analytics\r\n- **\"Nao agora\"** apenas fecha o popup — tutorial reaparece na proxima sessao\r\n\r\n---\r\n\r\n**Data**: 13/04/2026\r\n**Branch**: `feat/panel-tutorial`\r\n**Status**: Implementado — CRUD + Runtime + Analytics + Admin Panel + Workflow Integration\r\n"}