Trabalho acadêmico da disciplina Desenvolvimento Web II, do curso de Análise e Desenvolvimento de Sistemas do IFPR (Instituto Federal do Paraná).
Desenvolvido por: Luan de Sordi Rebuli e Hiran Zannata
O TicketFlow é um sistema de gestão de chamados técnicos de suporte (helpdesk). Clientes abrem chamados, analistas atendem, e gerentes acompanham a operação da equipe através de um board estilo Kanban com reatribuição via drag and drop. Todo o histórico de mudanças relevantes de cada chamado é auditado e exibido em linguagem natural.
O projeto foi construído seguindo o padrão de arquitetura CSR (Controller → Service → Repository), com foco em separar claramente onde vive cada tipo de responsabilidade: o Controller nunca acessa o banco nem decide regra de negócio, o Service nunca fala com o Eloquent diretamente, e o Repository não sabe nada sobre HTTP ou autorização.
| Perfil | O que pode fazer |
|---|---|
| Cliente | Abre chamados, comenta nos próprios chamados, fecha os próprios chamados. |
| Analista | Vê chamados sem dono e os que estão atribuídos a si; pode puxar (self-assign) um chamado em aberto; comenta nos que atende; move status entre Em Andamento e Resolvido (e pode reabrir um chamado resolvido). |
| Gerente | Acesso total: vê todos os chamados, atribui/reatribui analistas (inclusive via drag and drop no board), exclui chamados, acompanha métricas no dashboard. |
Aberto ──────────► Em Andamento ──────────► Resolvido ──────────► Fechado
│ ▲ │ ▲ │
│ └───┘ └───┘
│ (reabrir / avançar) (reabrir / avançar)
└──────────────────────────────────────────────────► Fechado
(cliente ou gerente fecham direto)
Aberto → Em Andamento: o analista atribuído (ou que acabou de puxar o chamado).Em Andamento → Resolvido: o analista atribuído.Resolvido → Em AndamentoouResolvido → Aberto: reabertura, permitida ao analista atribuído e ao gerente — útil quando um chamado foi marcado como resolvido por engano ou o problema voltou.Resolvido → Fechado(ou fechar direto deAberto/Em Andamento): o cliente dono do chamado, ou o gerente.Fechadoé terminal: nenhum papel consegue reabrir um chamado fechado.
Essa regra vive inteira no TicketService (não no Controller nem na View), porque é lógica de negócio pura — não depende de HTTP nem de banco de dados.
Request → Controller → Service → Repository → Eloquent/Banco
- Controller: recebe a requisição, valida via FormRequest, checa autorização via Policy, chama o Service e devolve View/Redirect. Não tem lógica de negócio nem query.
- Service: concentra as regras (transições de status, self-assign, reatribuição). Fala apenas com o Repository, nunca com o Eloquent.
- Repository: só faz queries Eloquent. Implementa uma interface (
TicketRepositoryInterface,TicketCommentRepositoryInterface), permitindo trocar a implementação sem tocar no Service — isso é o que dá testabilidade e inversão de dependência ao projeto. - Policy (
TicketPolicy): centraliza toda regra de autorização (quem pode ver, criar, comentar, mudar status, atribuir, excluir), usada tanto no back quanto para esconder botões na View. A validação no back-end é sempre obrigatória — o front-end só reflete o que a Policy permite, nunca decide sozinho.
Todas as tabelas (users, tickets, ticket_comments) usam UUID como chave primária em vez do tradicional auto-incremento. Isso evita que alguém consiga adivinhar o próximo ID de um recurso alterando a URL (/tickets/2 → /tickets/3), o que é uma prática mais segura para sistemas com dados sensíveis. O trade-off é um índice de busca levemente mais pesado que bigint, irrelevante para o volume deste trabalho.
O pacote owen-it/laravel-auditing está aplicado ao model Ticket, monitorando mudanças em status, priority e analyst_id. Cada alteração vira uma entrada de auditoria, exibida na tela de detalhes do chamado como frase em português (ex: "João alterou o status para 'Resolvido' em 12/10 às 14:00"), via um presenter customizado (App\Models\Audit::humanReadable()).
A auditoria funciona escutando os eventos do Eloquent (created, updated) — por isso, apenas mudanças feitas via save()/update() do Model são capturadas; alterações feitas via query builder cru (DB::table(...)->update()) não geram histórico.
A tela exclusiva do gerente (/board) apresenta os chamados como cards organizados em colunas — uma por analista, mais uma coluna "Não atribuído". Arrastar um card de uma coluna para outra dispara uma requisição PATCH /tickets/{ticket}/assign, implementada com SortableJS + fetch, que passa pelo Controller → Service → Repository normalmente, incluindo a checagem de autorização via Policy no back-end — o drag and drop nunca contorna essa validação.
Um resumo numérico no topo da tela mostra a quantidade de chamados por status e por prioridade.
| Camada | Tecnologia |
|---|---|
| Framework | Laravel 13 (PHP 8.3+) |
| Autenticação | Laravel Breeze (Blade + Tailwind CSS) |
| Banco de dados | SQLite |
| Auditoria | owen-it/laravel-auditing |
| Frontend | Blade, Tailwind CSS, Alpine.js, SortableJS |
| Testes | PHPUnit (Feature + Unit) |
# instalar dependências PHP e JS
composer install
npm install
# configurar ambiente
cp .env.example .env
php artisan key:generate
# criar banco e popular com dados de exemplo
php artisan migrate --seed
# compilar assets e subir o servidor (dois terminais)
npm run dev
php artisan serveAcesse http://127.0.0.1:8000. Usuários de teste criados pelo seeder:
| Perfil | Senha | |
|---|---|---|
| Cliente | cliente@helpdesk.test |
password |
| Analista | analista@helpdesk.test |
password |
| Analista | analista2@helpdesk.test |
password |
| Gerente | gerente@helpdesk.test |
password |
php artisan testProjeto acadêmico sem fins comerciais, desenvolvido para a disciplina de Desenvolvimento Web II do IFPR.