Entrar como:FuncionalidadesDoc
Pular para o conteúdo
← Voltar para a visão geral
✅ ConcluídoCard #46 · M8

Chamados (Help Desk com SLA)

Marco M8: Comunicação & Documentos (SULTS-inspired)

👤Wellington Santiago (via ZCode)

✅ EXECUÇÃO — 15/08/2026

Implementado e validado em produção (homolog). Commits: 2062f6c (fundação: 21 tabelas), 080c857 (features), bbfa277 (lint). Deploy CI verde.

Validação ao vivo: página(s) da feature respondendo 200 com a role correta + procedures tRPC testadas via HTTP (respostas JSON corretas). Ver relatório da sessão.

Detalhes de implementação e decisões: conforme o design acima deste documento (schema, procedures e páginas seguidos conforme especificado; ajustes menores documentados no commit).

📄 Card 46 [M8] — Chamados (Help Desk com SLA)

📚 Referência SULTS: https://www.sults.com.br/produtos/chamados Sistema de tickets com SLA duplo (primeira resposta + solução), formulários inteligentes por assunto, abertura em massa, escalonamento e CSAT. Funciona como Centro de Serviços Compartilhados (CSC) entre matriz ↔ unidades ↔ professores ↔ responsáveis.

🎯 Objetivo

Transformar o schema Ticket já existente (Card 17 placeholder) em um Help Desk completo com:

  • SLA duplo: tempo de primeira resposta + tempo de solução (independentes)
  • Pausa inteligente: prazo pausa em feriados e fora do expediente
  • Formulários inteligentes: campos obrigatórios por assunto (exigem fotos, números)
  • 3 fluxos: unidade→matriz, matriz→unidade, interno entre departamentos
  • Abertura em massa (lote): um chamado disparado pra múltiplas unidades (cada um vira cópia individual rastreável)
  • Kanban com drag-and-drop + cores por prioridade
  • Catálogo hierárquico de assuntos por departamento
  • CSAT (avaliação 1-5 ao final)
  • Dashboards: funil, ranking departamentos, tempo médio resolução, taxas SLA

📚 Documentação de Referência

  • prisma/schema.prismaTicket e TicketReply já existem (expandir)
  • docs/DESIGN_SYSTEM.md §6 — Kanban reusar padrão do CRM (Card 30)
  • src/components/ui/ — reusar componentes

🛠️ Especificação Técnica

Schema Prisma (expandir Ticket)

model Ticket {
  id              String        @id @default(cuid())
  code            String        @unique              // "TK-2026-0001"
  subject         String
  description     String        @db.Text
  status          TicketStatus  @default(OPEN)       // OPEN, IN_PROGRESS, WAITING, RESOLVED, CLOSED
  priority        TicketPriority @default(NORMAL)    // LOW, NORMAL, MEDIUM, HIGH, URGENT
  requesterId     String                              // quem abriu
  requester       User          @relation("TicketRequester", fields: [requesterId], references: [id])
  assignedId      String?                             // responsável
  assigned        User?         @relation("TicketAssignee", fields: [assignedId], references: [id])
  departmentId    String?
  department      TicketDepartment? @relation(fields: [departmentId], references: [id])
  branchId        String?
  branch          Branch?       @relation(fields: [branchId], references: [id])
  categoryId      String?
  category        TicketCategory? @relation(fields: [categoryId], references: [id])
  slaResponseDue  DateTime?                           // prazo 1ª resposta
  slaResolveDue   DateTime?                           // prazo solução
  slaRespondedAt  DateTime?
  slaResolvedAt   DateTime?
  slaPausedMs     Int           @default(0)           // tempo pausado total
  slaPausedAt     DateTime?
  batchId         String?                             // se gerado em lote
  replies         TicketReply[]                       // histórico
  attachments     String[]                            // URLs S3
  csatScore       Int?                                // 1-5
  csatComment     String?
  csatAt          DateTime?
  createdAt       DateTime      @default(now())
  updatedAt       DateTime      @updatedAt
  closedAt        DateTime?
}

model TicketReply {
  id              String   @id @default(cuid())
  ticketId        String
  ticket          Ticket   @relation(fields: [ticketId], references: [id])
  authorId        String
  author          User     @relation(fields: [authorId], references: [id])
  body            String   @db.Text
  isInternal      Boolean  @default(false)            // invisível ao solicitante
  attachments     String[]
  createdAt       DateTime @default(now())
}

model TicketDepartment {
  id              String   @id @default(cuid())
  name            String                                // "Financeiro", "Pedagógico", "TI"
  description     String?
  parentId        String?                               // hierarquia
  parent          TicketDepartment? @relation("DeptTree", fields: [parentId], references: [id])
  children        TicketDepartment[] @relation("DeptTree")
  headId          String?                               // líder do dept
  head            User?    @relation(fields: [headId], references: [id])
  tickets         Ticket[]
  members         DeptMember[]
}

model DeptMember {
  id              String   @id @default(cuid())
  departmentId    String
  userId          String
  role            DeptRole @default(MEMBER)            // HEAD, MEMBER
}

model TicketCategory {
  id              String   @id @default(cuid())
  name            String                                 // "Dúvida matrícula", "Erro sistema"
  departmentId    String
  department      TicketDepartment @relation(fields: [departmentId], references: [id])
  formFields      Json                                   // formulário dinâmico
  defaultSlaResponseHours  Int  @default(4)
  defaultSlaResolveHours   Int  @default(48)
}

enum TicketStatus { OPEN  IN_PROGRESS  WAITING  RESOLVED  CLOSED }
enum TicketPriority { LOW  NORMAL  MEDIUM  HIGH  URGENT }
enum DeptRole { HEAD  MEMBER }

Rotas (page.tsx)

  • /master/chamados — kanban + filtros + dashboards
  • /local/chamados — por filial
  • /professor/chamados, /pais/chamados, /aluno/chamados — abrir e acompanhar
  • /master/chamados/[id] — detalhe (thread + SLA + atribuição + CSAT)

Procedures tRPC (master.tickets, local.tickets, parent.tickets, etc.)

  • list (filtros: status, priority, dept, branch, sla vencendo)
  • create (abre ticket; dispara SLA timers)
  • createBatch (lote para múltiplas unidades)
  • reply (adiciona Reply; pode ser interna)
  • assign (atribui responsável)
  • transfer (muda dept com registro)
  • pause / resume (pausa SLA)
  • resolve / close
  • rateCsat (1-5 + comentário)
  • dashboard (funil, ranking dept, SLA stats)

SLA Engine

  • Cron Bull ticket-sla-check (a cada 5 min): identifica tickets perto do vencimento → notifica responsável
  • Pausa automática fora do expediente (configurável por dept) e feriados
  • Job Bull ticket-csat-request: 24h após RESOLVED, pede CSAT

✅ Critérios de Aceite

  1. MASTER configura departamentos + categorias + SLA default
  2. Usuário abre ticket com formulário dinâmico por categoria
  3. Abertura em lote cria cópias individuais rastreáveis (batchId)
  4. Kanban com drag-and-drop + cores por prioridade
  5. SLA duplo visível + alerta antes de estourar
  6. Mensagem interna invisível ao solicitante
  7. CSAT 1-5 ao resolver
  8. Dashboards: funil, ranking dept, tempo médio, taxas SLA
  9. Build passa + lint OK

🔌 Handoff

  • Sub-routers *.tickets em master, local, parent, professor
  • Cron ticket-sla-check + ticket-csat-request no bootstrap
  • Integração com notifyUser (push + email + in-app)

🎯 Próximo

Card 47: Enquetes (forms com trava de tela)

← Voltar para a visão geral