|
|
@@ -0,0 +1,1237 @@
|
|
|
+# Análise da Codebase e Contrato Backend → Frontend
|
|
|
+
|
|
|
+## Objetivo
|
|
|
+
|
|
|
+Este documento resume a análise da codebase do projeto e define **o que o backend precisa entregar para o frontend**.
|
|
|
+
|
|
|
+Hoje o backend tem apenas as rotas:
|
|
|
+
|
|
|
+- `POST /login`
|
|
|
+- `POST /register`
|
|
|
+
|
|
|
+Mas o frontend já possui diversas telas prontas. Portanto, este arquivo serve como **mapa de implementação do backend**, indicando:
|
|
|
+
|
|
|
+- o estado atual do projeto;
|
|
|
+- o que cada tela do frontend precisa receber;
|
|
|
+- quais endpoints precisam existir;
|
|
|
+- quais formatos de payload e resposta são recomendados;
|
|
|
+- quais partes da modelagem atual já suportam isso e quais ainda não suportam.
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 1. Estrutura da codebase
|
|
|
+
|
|
|
+## Frontend
|
|
|
+
|
|
|
+Pasta: `nettown_frontend`
|
|
|
+
|
|
|
+Stack identificada:
|
|
|
+
|
|
|
+- `SvelteKit`
|
|
|
+- `adapter-static`
|
|
|
+- `Svelte 5`
|
|
|
+- `Tailwind`
|
|
|
+- `layerchart`
|
|
|
+- `lucide-svelte`
|
|
|
+
|
|
|
+### Achados principais do frontend
|
|
|
+
|
|
|
+- O frontend está **visualmente completo** para várias áreas do produto.
|
|
|
+- O frontend está **quase 100% mockado**.
|
|
|
+- **Não existe client HTTP real** no frontend hoje.
|
|
|
+- **Não há `fetch`, `axios`, `Authorization` ou persistência de JWT** implementados nas telas principais.
|
|
|
+- A tela de login ainda usa validação mockada:
|
|
|
+ - e-mail: `admin@nettown.com`
|
|
|
+ - senha: `admin`
|
|
|
+- Várias telas consomem dados de:
|
|
|
+ - `src/lib/core/models/mock-data.js`
|
|
|
+ - `src/lib/features/sentiment/data/sentiment-dashboard.mock.js`
|
|
|
+- Existem rotas duplicadas em PT/EN para algumas telas:
|
|
|
+ - `profile` e `perfil`
|
|
|
+ - `subscription` e `assinatura`
|
|
|
+ - `help` e `ajuda`
|
|
|
+
|
|
|
+### Conclusão sobre o frontend
|
|
|
+
|
|
|
+O frontend está pronto como **produto visual e funcional local**, mas **não está integrado ao backend**.
|
|
|
+
|
|
|
+Ou seja:
|
|
|
+
|
|
|
+- a parte de layout/componentes está pronta;
|
|
|
+- a parte de contratos de API ainda precisa ser implementada.
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## Backend
|
|
|
+
|
|
|
+Pasta: `php_api`
|
|
|
+
|
|
|
+Stack identificada:
|
|
|
+
|
|
|
+- `PHP`
|
|
|
+- `FrameworkX`
|
|
|
+- `Firebase JWT`
|
|
|
+- `PostgreSQL`
|
|
|
+- `PDO`
|
|
|
+
|
|
|
+### Rotas atuais do backend
|
|
|
+
|
|
|
+- `POST /login`
|
|
|
+- `POST /register`
|
|
|
+- `GET /jwthelloworld` protegido por JWT
|
|
|
+
|
|
|
+### Padrão de resposta atual do backend
|
|
|
+
|
|
|
+O backend hoje responde com envelope padrão:
|
|
|
+
|
|
|
+```json
|
|
|
+{
|
|
|
+ "status": "ok",
|
|
|
+ "msg": "[100] Request ok.",
|
|
|
+ "code": "S_OK",
|
|
|
+ "data": {}
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+Em erro:
|
|
|
+
|
|
|
+```json
|
|
|
+{
|
|
|
+ "status": "failed",
|
|
|
+ "msg": "mensagem de erro",
|
|
|
+ "code": "E_VALIDATE",
|
|
|
+ "data": {}
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### Autenticação atual
|
|
|
+
|
|
|
+- Login usa `email` + `password`.
|
|
|
+- JWT contém:
|
|
|
+ - `sub`
|
|
|
+ - `email`
|
|
|
+ - `company_id`
|
|
|
+ - `role`
|
|
|
+- Middleware JWT valida usuário ativo por `user_deleted_at = 'infinity'`.
|
|
|
+
|
|
|
+### Conclusão sobre o backend
|
|
|
+
|
|
|
+O backend já está preparado para:
|
|
|
+
|
|
|
+- autenticação com JWT;
|
|
|
+- conexão PostgreSQL;
|
|
|
+- leitura do banco novo.
|
|
|
+
|
|
|
+Mas **a camada de API de produto ainda não foi construída**.
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 2. Banco atual vs necessidade do frontend
|
|
|
+
|
|
|
+## Tabelas que já suportam bem o frontend
|
|
|
+
|
|
|
+- `company`
|
|
|
+- `user`
|
|
|
+- `operator`
|
|
|
+- `operator_channel`
|
|
|
+- `sla_config`
|
|
|
+- `integration`
|
|
|
+- `client`
|
|
|
+- `conversation`
|
|
|
+- `message`
|
|
|
+- `message_attachment`
|
|
|
+- `message_reaction`
|
|
|
+- `conversation_participant`
|
|
|
+- `webhook_event`
|
|
|
+- `conversation_analysis`
|
|
|
+- `aspect_feedback`
|
|
|
+- `emotion_snapshot`
|
|
|
+- `public_opinion`
|
|
|
+- `alert`
|
|
|
+- `ai_action`
|
|
|
+- `persona`
|
|
|
+- `client_persona`
|
|
|
+- `best_action`
|
|
|
+- `volume_snapshot`
|
|
|
+- `sentiment_evolution`
|
|
|
+- `playbooks_monitor`
|
|
|
+- `operator_daily_stats`
|
|
|
+- `kpi_snapshot`
|
|
|
+
|
|
|
+## Gaps entre frontend e schema atual
|
|
|
+
|
|
|
+Estas são as principais lacunas identificadas:
|
|
|
+
|
|
|
+- **Filtro de unidade**
|
|
|
+ - O frontend usa filtro de `unidade` em várias telas.
|
|
|
+ - O schema atual **não tem tabela explícita de unidade/loja/filial**.
|
|
|
+
|
|
|
+- **Configuração de alerta de SLA por percentual**
|
|
|
+ - A tela de SLA usa `alertPct`.
|
|
|
+ - A tabela `sla_config` hoje guarda apenas:
|
|
|
+ - `sla_config_response_hours`
|
|
|
+ - `sla_config_resolution_hours`
|
|
|
+ - **Não existe coluna de percentual de alerta**.
|
|
|
+
|
|
|
+- **Configurações / notificações**
|
|
|
+ - O frontend de configurações mostra preferências e integrações.
|
|
|
+ - O schema atual **não tem tabela dedicada para preferências/notificações**.
|
|
|
+
|
|
|
+- **Assinatura / billing**
|
|
|
+ - O frontend possui telas de assinatura e planos.
|
|
|
+ - O schema atual **não tem modelagem de assinatura/plano/cobrança**.
|
|
|
+
|
|
|
+- **Ajuda / FAQ / suporte**
|
|
|
+ - O frontend tem tela pronta.
|
|
|
+ - Isso pode ser estático, mas **não existe modelagem específica** hoje.
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 3. Mapa de telas do frontend
|
|
|
+
|
|
|
+## Públicas
|
|
|
+
|
|
|
+- `/`
|
|
|
+ - landing page institucional
|
|
|
+ - não depende de backend para funcionar
|
|
|
+
|
|
|
+- `/login`
|
|
|
+ - hoje mockada
|
|
|
+ - deve passar a consumir `POST /login`
|
|
|
+
|
|
|
+## Privadas
|
|
|
+
|
|
|
+- `/dashboard`
|
|
|
+- `/dashboard/interactions`
|
|
|
+- `/dashboard/analytics`
|
|
|
+- `/dashboard/personas`
|
|
|
+- `/dashboard/evolucao`
|
|
|
+- `/dashboard/executive`
|
|
|
+- `/dashboard/agents`
|
|
|
+- `/dashboard/sla`
|
|
|
+- `/dashboard/settings`
|
|
|
+- `/dashboard/profile`
|
|
|
+- `/dashboard/subscription`
|
|
|
+- `/dashboard/help`
|
|
|
+
|
|
|
+Rotas duplicadas equivalentes:
|
|
|
+
|
|
|
+- `/dashboard/perfil`
|
|
|
+- `/dashboard/assinatura`
|
|
|
+- `/dashboard/ajuda`
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 4. Situação real por tela
|
|
|
+
|
|
|
+## 4.1 Login
|
|
|
+
|
|
|
+### Estado atual
|
|
|
+
|
|
|
+- tela pronta;
|
|
|
+- autenticação ainda fake;
|
|
|
+- não salva token;
|
|
|
+- não consulta `GET /me`;
|
|
|
+- não protege navegação real por sessão.
|
|
|
+
|
|
|
+### Backend precisa entregar
|
|
|
+
|
|
|
+- `POST /login`
|
|
|
+- `GET /me`
|
|
|
+
|
|
|
+### Contrato recomendado
|
|
|
+
|
|
|
+#### `POST /login`
|
|
|
+
|
|
|
+Request:
|
|
|
+
|
|
|
+```json
|
|
|
+{
|
|
|
+ "email": "admin@empresa.com",
|
|
|
+ "password": "12345678"
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+Response:
|
|
|
+
|
|
|
+```json
|
|
|
+{
|
|
|
+ "status": "ok",
|
|
|
+ "msg": "[100] Request ok.",
|
|
|
+ "code": "S_OK",
|
|
|
+ "data": {
|
|
|
+ "token": "jwt-token",
|
|
|
+ "user": {
|
|
|
+ "user_id": 1,
|
|
|
+ "company_id": 1,
|
|
|
+ "user_name": "Admin",
|
|
|
+ "user_phone": "5511999999999",
|
|
|
+ "user_email": "admin@empresa.com",
|
|
|
+ "user_role": "admin",
|
|
|
+ "user_created_at": "2026-05-25T10:00:00Z"
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+#### `GET /me`
|
|
|
+
|
|
|
+Objetivo:
|
|
|
+
|
|
|
+- reidratar sessão no refresh da página;
|
|
|
+- preencher header do app;
|
|
|
+- popular perfil.
|
|
|
+
|
|
|
+Response:
|
|
|
+
|
|
|
+```json
|
|
|
+{
|
|
|
+ "status": "ok",
|
|
|
+ "msg": "[100] Request ok.",
|
|
|
+ "code": "S_OK",
|
|
|
+ "data": {
|
|
|
+ "user_id": 1,
|
|
|
+ "company_id": 1,
|
|
|
+ "user_name": "Admin",
|
|
|
+ "user_phone": "5511999999999",
|
|
|
+ "user_email": "admin@empresa.com",
|
|
|
+ "user_role": "admin",
|
|
|
+ "company": {
|
|
|
+ "company_id": 1,
|
|
|
+ "company_name": "Empresa X",
|
|
|
+ "company_cnpj": "12345678000199",
|
|
|
+ "company_logo": "https://..."
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 4.2 Dashboard principal (`/dashboard`)
|
|
|
+
|
|
|
+### Estado atual
|
|
|
+
|
|
|
+Hoje a tela usa mocks para:
|
|
|
+
|
|
|
+- KPIs do topo;
|
|
|
+- fila priorizada;
|
|
|
+- radar emocional;
|
|
|
+- volume por canal;
|
|
|
+- distribuição por aspectos;
|
|
|
+- drilldown por aspecto.
|
|
|
+
|
|
|
+### Backend precisa entregar
|
|
|
+
|
|
|
+#### `GET /dashboard/overview`
|
|
|
+
|
|
|
+Query params sugeridos:
|
|
|
+
|
|
|
+- `period=today|yesterday|week`
|
|
|
+- `unit=all|flagship|franquias|pop-up|digital`
|
|
|
+- `area=all|atendimento|produto|logistica|marketing`
|
|
|
+- `sentiment=all|positive|neutral|negative`
|
|
|
+- `volume_view=hour|day`
|
|
|
+
|
|
|
+### Response recomendada
|
|
|
+
|
|
|
+```json
|
|
|
+{
|
|
|
+ "status": "ok",
|
|
|
+ "msg": "[100] Request ok.",
|
|
|
+ "code": "S_OK",
|
|
|
+ "data": {
|
|
|
+ "kpis": {
|
|
|
+ "registeredUsers": 55,
|
|
|
+ "activeAgents": 4,
|
|
|
+ "totalConversations": 418,
|
|
|
+ "generalSentimentScore": 0.1,
|
|
|
+ "unregisteredUsers": 149682
|
|
|
+ },
|
|
|
+ "priorityQueue": [
|
|
|
+ {
|
|
|
+ "id": 1,
|
|
|
+ "customerName": "Carolina Ribeiro",
|
|
|
+ "segment": "Elegante Estratégica",
|
|
|
+ "status": "VENDEDORA NÃO RESPONDEU",
|
|
|
+ "slaStatus": "SLA 12h estourado",
|
|
|
+ "timeAgo": "há 18h",
|
|
|
+ "sellerName": "Maria",
|
|
|
+ "lastMessage": "Não tem no meu tamanho 40?",
|
|
|
+ "motive": "Pediu tamanho indisponível — reservar/repor",
|
|
|
+ "impact": 8639,
|
|
|
+ "ticket": 879,
|
|
|
+ "chance": 82,
|
|
|
+ "optimumWindow": "6h",
|
|
|
+ "score": 7084,
|
|
|
+ "conversationId": 101,
|
|
|
+ "clientId": 51,
|
|
|
+ "operatorId": 7
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ "radarData": [
|
|
|
+ { "name": "Confiança", "value": 45 },
|
|
|
+ { "name": "Alegria", "value": 100 },
|
|
|
+ { "name": "Antecipação", "value": 41 },
|
|
|
+ { "name": "Medo", "value": 18 },
|
|
|
+ { "name": "Tristeza", "value": 3 },
|
|
|
+ { "name": "Raiva", "value": 13 }
|
|
|
+ ],
|
|
|
+ "volumeData": [
|
|
|
+ { "date": "2026-05-02T11:00:00Z", "whatsapp": 10 },
|
|
|
+ { "date": "2026-05-02T12:00:00Z", "whatsapp": 230 }
|
|
|
+ ],
|
|
|
+ "aspectsData": [
|
|
|
+ { "aspect": "Atendimento", "positive": 350, "neutral": 50, "negative": 20 },
|
|
|
+ { "aspect": "Produto", "positive": 130, "neutral": 30, "negative": 40 }
|
|
|
+ ],
|
|
|
+ "aspectsDrilldown": {
|
|
|
+ "Atendimento": {
|
|
|
+ "positive": [
|
|
|
+ { "label": "Satisfação com atendimento", "value": 285 }
|
|
|
+ ],
|
|
|
+ "neutral": [],
|
|
|
+ "negative": []
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### Fontes no banco
|
|
|
+
|
|
|
+- `kpi_snapshot`
|
|
|
+- `alert`
|
|
|
+- `conversation`
|
|
|
+- `client`
|
|
|
+- `operator`
|
|
|
+- `emotion_snapshot`
|
|
|
+- `volume_snapshot`
|
|
|
+- `conversation_analysis`
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 4.3 Interações (`/dashboard/interactions`)
|
|
|
+
|
|
|
+### Estado atual
|
|
|
+
|
|
|
+Hoje a tela mostra:
|
|
|
+
|
|
|
+- listagem de conversas analisadas;
|
|
|
+- filtros rápidos;
|
|
|
+- modal com chat;
|
|
|
+- mini relatório lateral.
|
|
|
+
|
|
|
+### Backend precisa entregar
|
|
|
+
|
|
|
+- `GET /interactions`
|
|
|
+- `GET /interactions/details`
|
|
|
+
|
|
|
+#### `GET /interactions`
|
|
|
+
|
|
|
+Query params sugeridos:
|
|
|
+
|
|
|
+- `page`
|
|
|
+- `per_page`
|
|
|
+- `search`
|
|
|
+- `filter=all|my_clients|new|unfinished`
|
|
|
+- `sentiment`
|
|
|
+- `operator_id`
|
|
|
+- `company_id`
|
|
|
+
|
|
|
+Response:
|
|
|
+
|
|
|
+```json
|
|
|
+{
|
|
|
+ "status": "ok",
|
|
|
+ "msg": "[100] Request ok.",
|
|
|
+ "code": "S_OK",
|
|
|
+ "data": {
|
|
|
+ "items": [
|
|
|
+ {
|
|
|
+ "conversationId": 201,
|
|
|
+ "client": "554196690452",
|
|
|
+ "agent": "Leticia",
|
|
|
+ "sentiment": "CONTENTAMENTO",
|
|
|
+ "score": 0.5,
|
|
|
+ "aspect": "Atendimento",
|
|
|
+ "subaspect": "Informativo",
|
|
|
+ "datetime": "2026-05-02T19:59:00Z"
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ "pagination": {
|
|
|
+ "page": 1,
|
|
|
+ "per_page": 20,
|
|
|
+ "total": 120,
|
|
|
+ "total_pages": 6
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+#### `GET /interactions/details`
|
|
|
+
|
|
|
+Query params sugeridos:
|
|
|
+
|
|
|
+- `conversation_id`
|
|
|
+
|
|
|
+Response:
|
|
|
+
|
|
|
+```json
|
|
|
+{
|
|
|
+ "status": "ok",
|
|
|
+ "msg": "[100] Request ok.",
|
|
|
+ "code": "S_OK",
|
|
|
+ "data": {
|
|
|
+ "conversation": {
|
|
|
+ "conversationId": 201,
|
|
|
+ "client": "554196690452",
|
|
|
+ "channel": "WhatsApp",
|
|
|
+ "agent": "Leticia"
|
|
|
+ },
|
|
|
+ "thread": [
|
|
|
+ {
|
|
|
+ "id": "m1",
|
|
|
+ "isAgent": false,
|
|
|
+ "text": "Oi, queria saber se esse look chega na loja física.",
|
|
|
+ "time": "19:58",
|
|
|
+ "date": "2026-05-02"
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "id": "m2",
|
|
|
+ "isAgent": true,
|
|
|
+ "text": "Chega sim, posso te mostrar opções.",
|
|
|
+ "time": "19:59",
|
|
|
+ "date": "2026-05-02"
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ "report": {
|
|
|
+ "avgResponse": "04:03",
|
|
|
+ "totalDuration": "08:28",
|
|
|
+ "avgAgent": "00:00",
|
|
|
+ "avgClient": "08:05",
|
|
|
+ "mainAspect": "Atendimento",
|
|
|
+ "subAspect": "Informativo",
|
|
|
+ "lastMessageAuthor": "Cliente",
|
|
|
+ "consecutiveMessages": false,
|
|
|
+ "sentiment": "CONTENTAMENTO",
|
|
|
+ "score": 0.5
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### Fontes no banco
|
|
|
+
|
|
|
+- `conversation`
|
|
|
+- `message`
|
|
|
+- `conversation_analysis`
|
|
|
+- `client`
|
|
|
+- `operator`
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 4.4 Análise de sentimento (`/dashboard/analytics`)
|
|
|
+
|
|
|
+### Estado atual
|
|
|
+
|
|
|
+Hoje a tela trabalha com um view model único contendo:
|
|
|
+
|
|
|
+- cards-resumo;
|
|
|
+- alertas;
|
|
|
+- linha temporal de ganhos/perdas;
|
|
|
+- lista de aspectos com frases reais.
|
|
|
+
|
|
|
+### Backend precisa entregar
|
|
|
+
|
|
|
+#### `GET /analytics/sentiment/dashboard`
|
|
|
+
|
|
|
+Query params sugeridos:
|
|
|
+
|
|
|
+- `timeframe=day|week|month`
|
|
|
+- `company_id`
|
|
|
+- `aspect`
|
|
|
+- `sentiment`
|
|
|
+
|
|
|
+Response:
|
|
|
+
|
|
|
+```json
|
|
|
+{
|
|
|
+ "status": "ok",
|
|
|
+ "msg": "[100] Request ok.",
|
|
|
+ "code": "S_OK",
|
|
|
+ "data": {
|
|
|
+ "summaryCards": [
|
|
|
+ {
|
|
|
+ "id": "atRiskClients",
|
|
|
+ "label": "Clientes em risco",
|
|
|
+ "value": 23,
|
|
|
+ "image": "/images/sentiment/risk.svg"
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "id": "opportunities",
|
|
|
+ "label": "Oportunidades",
|
|
|
+ "value": 41,
|
|
|
+ "image": "/images/sentiment/opportunity.svg"
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ "alerts": [
|
|
|
+ {
|
|
|
+ "id": "alert-1",
|
|
|
+ "clientId": 12,
|
|
|
+ "clientName": "Grupo Horizonte",
|
|
|
+ "title": "Grupo Horizonte com alta chance de churn",
|
|
|
+ "description": "Queda de engajamento nas últimas 2 semanas.",
|
|
|
+ "priority": "high",
|
|
|
+ "priorityLabel": "Alta prioridade",
|
|
|
+ "category": "churn_risk"
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ "timelineViews": {
|
|
|
+ "day": [
|
|
|
+ { "period": "Dia 1", "gains": 28, "losses": 12 }
|
|
|
+ ],
|
|
|
+ "week": [
|
|
|
+ { "period": "Sem 1", "gains": 38, "losses": 16 }
|
|
|
+ ],
|
|
|
+ "month": [
|
|
|
+ { "period": "Jan", "gains": 180, "losses": 75 }
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ "aspects": [
|
|
|
+ {
|
|
|
+ "id": "atendimento",
|
|
|
+ "name": "Atendimento",
|
|
|
+ "volume": 14,
|
|
|
+ "positive": [
|
|
|
+ { "text": "Fui respondida super rápido.", "client": "Maria Silva" }
|
|
|
+ ],
|
|
|
+ "neutral": [],
|
|
|
+ "negative": []
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### Fontes no banco
|
|
|
+
|
|
|
+- `alert`
|
|
|
+- `public_opinion`
|
|
|
+- `aspect_feedback`
|
|
|
+- `conversation_analysis`
|
|
|
+- `client`
|
|
|
+- `conversation`
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 4.5 Personas (`/dashboard/personas`)
|
|
|
+
|
|
|
+### Estado atual
|
|
|
+
|
|
|
+A tela precisa de:
|
|
|
+
|
|
|
+- KPIs de personas;
|
|
|
+- estatísticas gerais;
|
|
|
+- cards de personas;
|
|
|
+- detalhes da persona selecionada;
|
|
|
+- next best action.
|
|
|
+
|
|
|
+### Backend precisa entregar
|
|
|
+
|
|
|
+#### `GET /personas/overview`
|
|
|
+
|
|
|
+Query params sugeridos:
|
|
|
+
|
|
|
+- `period=week|month|quarter`
|
|
|
+- `unit`
|
|
|
+- `area`
|
|
|
+- `sentiment=all|positive|neutral|negative`
|
|
|
+
|
|
|
+Response:
|
|
|
+
|
|
|
+```json
|
|
|
+{
|
|
|
+ "status": "ok",
|
|
|
+ "msg": "[100] Request ok.",
|
|
|
+ "code": "S_OK",
|
|
|
+ "data": {
|
|
|
+ "kpis": {
|
|
|
+ "active": 5,
|
|
|
+ "churn": 60.1,
|
|
|
+ "loss": 7521.27,
|
|
|
+ "potentialLabel": "Neutro"
|
|
|
+ },
|
|
|
+ "stats": {
|
|
|
+ "identified": 28,
|
|
|
+ "messages": 1840,
|
|
|
+ "aspects": 18,
|
|
|
+ "subaspects": 56
|
|
|
+ },
|
|
|
+ "personas": [
|
|
|
+ {
|
|
|
+ "id": "1",
|
|
|
+ "nome": "Cliente Tristeza Invisível",
|
|
|
+ "tipo": "O PERFIL",
|
|
|
+ "descricao": "Sem conexão com CRM",
|
|
|
+ "detalhes": "Cliente antigo, compras esporádicas.",
|
|
|
+ "expansao": "Oferecer atendimento personalizado.",
|
|
|
+ "engajamento": "Acionar contato humano imediato.",
|
|
|
+ "risco": "Alto"
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### Fontes no banco
|
|
|
+
|
|
|
+- `persona`
|
|
|
+- `client_persona`
|
|
|
+- `best_action`
|
|
|
+- `conversation_analysis`
|
|
|
+- `message`
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 4.6 Evolução (`/dashboard/evolucao`)
|
|
|
+
|
|
|
+### Estado atual
|
|
|
+
|
|
|
+A tela precisa de:
|
|
|
+
|
|
|
+- KPIs de evolução;
|
|
|
+- série de evolução dos sentimentos;
|
|
|
+- série de monitoramento de playbooks;
|
|
|
+- totais do período.
|
|
|
+
|
|
|
+### Backend precisa entregar
|
|
|
+
|
|
|
+#### `GET /evolution/overview`
|
|
|
+
|
|
|
+Response:
|
|
|
+
|
|
|
+```json
|
|
|
+{
|
|
|
+ "status": "ok",
|
|
|
+ "msg": "[100] Request ok.",
|
|
|
+ "code": "S_OK",
|
|
|
+ "data": {
|
|
|
+ "kpis": {
|
|
|
+ "churnEvitado": 85000,
|
|
|
+ "roiUpsell": 3.1,
|
|
|
+ "scoreMedio": 74,
|
|
|
+ "taxaEvolucao": 21.0,
|
|
|
+ "conversaoEmocao": 47
|
|
|
+ },
|
|
|
+ "sentimentSeries": [
|
|
|
+ { "date": "2024-04-19", "value": 0.05 },
|
|
|
+ { "date": "2024-04-20", "value": 0.08 }
|
|
|
+ ],
|
|
|
+ "playbooksSeries": [
|
|
|
+ { "date": "2024-04-19", "novos": 5, "convertidos": 1 },
|
|
|
+ { "date": "2024-04-20", "novos": 15, "convertidos": 3 }
|
|
|
+ ],
|
|
|
+ "playbooksTotals": {
|
|
|
+ "novos": 20,
|
|
|
+ "convertidos": 4
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### Fontes no banco
|
|
|
+
|
|
|
+- `sentiment_evolution`
|
|
|
+- `playbooks_monitor`
|
|
|
+- `kpi_snapshot`
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 4.7 Executive Dashboard (`/dashboard/executive`)
|
|
|
+
|
|
|
+### Estado atual
|
|
|
+
|
|
|
+A tela precisa de:
|
|
|
+
|
|
|
+- KPIs executivos;
|
|
|
+- distribuição de churn;
|
|
|
+- LTV em risco;
|
|
|
+- status de SLA;
|
|
|
+- emoção geral da base;
|
|
|
+- acessos rápidos.
|
|
|
+
|
|
|
+### Backend precisa entregar
|
|
|
+
|
|
|
+#### `GET /executive/dashboard`
|
|
|
+
|
|
|
+Response:
|
|
|
+
|
|
|
+```json
|
|
|
+{
|
|
|
+ "status": "ok",
|
|
|
+ "msg": "[100] Request ok.",
|
|
|
+ "code": "S_OK",
|
|
|
+ "data": {
|
|
|
+ "topKpis": [
|
|
|
+ {
|
|
|
+ "title": "Venda Atual",
|
|
|
+ "value": "R$ 879",
|
|
|
+ "trendLabel": "↑ +12% vs ontem",
|
|
|
+ "danger": false
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ "churnDistribution": [
|
|
|
+ { "label": "Baixo", "value": 45, "color": "#10b981" },
|
|
|
+ { "label": "Crítico", "value": 10, "color": "#ef4444" }
|
|
|
+ ],
|
|
|
+ "ltvRisk": {
|
|
|
+ "ltvTotal": 285000,
|
|
|
+ "ltvAtRisk": 34556,
|
|
|
+ "ltvRiskPct": 12,
|
|
|
+ "criticalClients": 34,
|
|
|
+ "avgTicket": 1016,
|
|
|
+ "trendText": "+3 clientes entraram em risco crítico desde ontem"
|
|
|
+ },
|
|
|
+ "sla": {
|
|
|
+ "withinPct": 78,
|
|
|
+ "breachPct": 22,
|
|
|
+ "byDepartment": [
|
|
|
+ { "department": "SAC", "value": 72 },
|
|
|
+ { "department": "Vendas", "value": 85 },
|
|
|
+ { "department": "Suporte", "value": 91 }
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ "emotions": {
|
|
|
+ "items": [
|
|
|
+ { "label": "Alegria", "value": 38, "count": 1524, "color": "#10b981" }
|
|
|
+ ],
|
|
|
+ "avgSentimentScore": 0.28
|
|
|
+ },
|
|
|
+ "quickAccess": {
|
|
|
+ "conversationsToday": 418,
|
|
|
+ "activePersonas": 5,
|
|
|
+ "activeAgents": 12,
|
|
|
+ "activePlaybooks": 3,
|
|
|
+ "pendingSettings": 2,
|
|
|
+ "evolutionDelta": "+12%"
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### Fontes no banco
|
|
|
+
|
|
|
+- `kpi_snapshot`
|
|
|
+- `emotion_snapshot`
|
|
|
+- `operator_daily_stats`
|
|
|
+- `alert`
|
|
|
+- `sla_config`
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 4.8 Agentes (`/dashboard/agents`)
|
|
|
+
|
|
|
+### Estado atual
|
|
|
+
|
|
|
+A tela precisa de:
|
|
|
+
|
|
|
+- listagem de agentes;
|
|
|
+- filtros;
|
|
|
+- KPIs resumidos;
|
|
|
+- criação de agente;
|
|
|
+- edição;
|
|
|
+- ativação/desativação;
|
|
|
+- toggle de escalonamento.
|
|
|
+
|
|
|
+### Backend precisa entregar
|
|
|
+
|
|
|
+- `GET /agents`
|
|
|
+- `POST /agents`
|
|
|
+- `POST /agents/status`
|
|
|
+- `POST /agents/escalation`
|
|
|
+
|
|
|
+#### `GET /agents`
|
|
|
+
|
|
|
+Response:
|
|
|
+
|
|
|
+```json
|
|
|
+{
|
|
|
+ "status": "ok",
|
|
|
+ "msg": "[100] Request ok.",
|
|
|
+ "code": "S_OK",
|
|
|
+ "data": {
|
|
|
+ "items": [
|
|
|
+ {
|
|
|
+ "id": 1,
|
|
|
+ "name": "Maria Santos",
|
|
|
+ "email": "maria@empresa.com",
|
|
|
+ "initials": "MS",
|
|
|
+ "department": "SAC",
|
|
|
+ "channels": ["whatsapp"],
|
|
|
+ "status": "Ativo",
|
|
|
+ "availableForEscalation": true,
|
|
|
+ "todayAttendances": 24,
|
|
|
+ "avgResponseTime": "3m 12s",
|
|
|
+ "responseTimeTrend": "down",
|
|
|
+ "slaPct": 94
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ "stats": {
|
|
|
+ "total": 8,
|
|
|
+ "active": 6,
|
|
|
+ "inAttendance": 2,
|
|
|
+ "availableForEscalation": 5
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+#### `POST /agents`
|
|
|
+
|
|
|
+Request:
|
|
|
+
|
|
|
+```json
|
|
|
+{
|
|
|
+ "name": "Carolina Ribeiro",
|
|
|
+ "email": "carolina@empresa.com",
|
|
|
+ "department": "SAC",
|
|
|
+ "channels": ["whatsapp", "instagram"],
|
|
|
+ "status": "Ativo",
|
|
|
+ "availableForEscalation": true
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+Para editar um agente existente, usar `POST /agents` novamente enviando o `id` no payload.
|
|
|
+
|
|
|
+Para alterar status, usar `POST /agents/status` enviando o `id` no payload.
|
|
|
+
|
|
|
+Para alterar escalonamento, usar `POST /agents/escalation` enviando o `id` no payload.
|
|
|
+
|
|
|
+### Fontes no banco
|
|
|
+
|
|
|
+- `operator`
|
|
|
+- `operator_channel`
|
|
|
+- `operator_daily_stats`
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 4.9 SLA (`/dashboard/sla`)
|
|
|
+
|
|
|
+### Estado atual
|
|
|
+
|
|
|
+A tela precisa de:
|
|
|
+
|
|
|
+- lista de departamentos configurados;
|
|
|
+- edição inline;
|
|
|
+- criação de departamento/configuração;
|
|
|
+- status em tempo real por departamento.
|
|
|
+
|
|
|
+### Backend precisa entregar
|
|
|
+
|
|
|
+- `GET /sla/configs`
|
|
|
+- `POST /sla/configs`
|
|
|
+- `GET /sla/live-status`
|
|
|
+
|
|
|
+#### `GET /sla/configs`
|
|
|
+
|
|
|
+Response:
|
|
|
+
|
|
|
+```json
|
|
|
+{
|
|
|
+ "status": "ok",
|
|
|
+ "msg": "[100] Request ok.",
|
|
|
+ "code": "S_OK",
|
|
|
+ "data": {
|
|
|
+ "items": [
|
|
|
+ {
|
|
|
+ "id": "sac",
|
|
|
+ "name": "SAC",
|
|
|
+ "firstResponseH": 1,
|
|
|
+ "firstResponseM": 30,
|
|
|
+ "resolutionH": 24,
|
|
|
+ "alertPct": 80,
|
|
|
+ "liveStatus": "breach",
|
|
|
+ "liveDetail": "14h estourado",
|
|
|
+ "lastUpdated": "há 8 minutos"
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+Para editar uma configuração existente, usar `POST /sla/configs` novamente enviando o `id` no payload.
|
|
|
+
|
|
|
+### Observação importante
|
|
|
+
|
|
|
+O banco atual **não possui `alertPct` na tabela `sla_config`**.
|
|
|
+
|
|
|
+Então existem duas opções:
|
|
|
+
|
|
|
+- **Opção A**: adicionar uma coluna nova na tabela `sla_config`;
|
|
|
+- **Opção B**: manter esse percentual em configuração de aplicação.
|
|
|
+
|
|
|
+### Fontes no banco
|
|
|
+
|
|
|
+- `sla_config`
|
|
|
+- `conversation`
|
|
|
+- `operator`
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 4.10 Configurações (`/dashboard/settings`)
|
|
|
+
|
|
|
+### Estado atual
|
|
|
+
|
|
|
+A tela é estática e mostra:
|
|
|
+
|
|
|
+- integrações;
|
|
|
+- notificações;
|
|
|
+- suporte.
|
|
|
+
|
|
|
+### Backend precisa entregar
|
|
|
+
|
|
|
+#### `GET /settings/overview`
|
|
|
+
|
|
|
+Response:
|
|
|
+
|
|
|
+```json
|
|
|
+{
|
|
|
+ "status": "ok",
|
|
|
+ "msg": "[100] Request ok.",
|
|
|
+ "code": "S_OK",
|
|
|
+ "data": {
|
|
|
+ "integrations": [
|
|
|
+ {
|
|
|
+ "title": "WhatsApp Business",
|
|
|
+ "description": "Configure sua conta do WhatsApp Business para comunicação",
|
|
|
+ "status": "connected",
|
|
|
+ "action": "gerenciar"
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "title": "WhatsApp dos Vendedores",
|
|
|
+ "description": "Adicione os números de WhatsApp da equipe de vendas",
|
|
|
+ "status": "partial",
|
|
|
+ "action": "configurar",
|
|
|
+ "count": "3 de 5 configurados"
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ "notifications": [
|
|
|
+ {
|
|
|
+ "title": "Alertas de Sentimento",
|
|
|
+ "description": "Receba alertas sobre mudanças no sentimento dos clientes",
|
|
|
+ "status": "enabled",
|
|
|
+ "action": "configurar"
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ "support": [
|
|
|
+ {
|
|
|
+ "title": "Central de Ajuda",
|
|
|
+ "description": "Acesse nossa documentação e tutoriais",
|
|
|
+ "status": "available",
|
|
|
+ "action": "acessar"
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### Observação importante
|
|
|
+
|
|
|
+Esta parte do produto **não está coberta pelo schema atual** de forma completa.
|
|
|
+
|
|
|
+Existe suporte parcial em:
|
|
|
+
|
|
|
+- `integration`
|
|
|
+- `operator_channel`
|
|
|
+
|
|
|
+Mas **não há modelagem explícita** para preferências de notificação e configurações gerais.
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 4.11 Perfil (`/dashboard/profile` e `/dashboard/perfil`)
|
|
|
+
|
|
|
+### Backend precisa entregar
|
|
|
+
|
|
|
+- `POST /user/get`
|
|
|
+- `POST /me`
|
|
|
+- `POST /me/change-password`
|
|
|
+
|
|
|
+#### `POST /me`
|
|
|
+
|
|
|
+Request:
|
|
|
+
|
|
|
+```json
|
|
|
+{
|
|
|
+ "user_name": "Admin",
|
|
|
+ "user_phone": "5511999999999"
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+#### `POST /me/change-password`
|
|
|
+
|
|
|
+Request:
|
|
|
+
|
|
|
+```json
|
|
|
+{
|
|
|
+ "currentPassword": "senha-atual",
|
|
|
+ "newPassword": "senha-nova",
|
|
|
+ "confirmPassword": "senha-nova"
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### Fontes no banco
|
|
|
+
|
|
|
+- `user`
|
|
|
+- `company`
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 4.13 Ajuda (`/dashboard/help` e `/dashboard/ajuda`)
|
|
|
+
|
|
|
+### Backend precisa entregar
|
|
|
+
|
|
|
+Opcionalmente:
|
|
|
+
|
|
|
+- `GET /support/faq`
|
|
|
+- `GET /support/channels`
|
|
|
+
|
|
|
+### Observação
|
|
|
+
|
|
|
+Essa tela pode continuar **100% estática** no frontend, se preferir.
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 5. Rotas recomendadas para o backend
|
|
|
+
|
|
|
+## Prioridade 0 — obrigatórias para integrar o app
|
|
|
+
|
|
|
+- `POST /login`
|
|
|
+- `POST /user/get`
|
|
|
+- `POST /me`
|
|
|
+- `POST /me/change-password`
|
|
|
+
|
|
|
+## Prioridade 1 — telas mais centrais do produto
|
|
|
+
|
|
|
+- `GET /dashboard/overview`
|
|
|
+- `GET /interactions`
|
|
|
+- `GET /interactions/details`
|
|
|
+- `GET /analytics/sentiment/dashboard`
|
|
|
+
|
|
|
+## Prioridade 2 — gestão e inteligência
|
|
|
+
|
|
|
+- `GET /personas/overview`
|
|
|
+- `GET /evolution/overview`
|
|
|
+- `GET /executive/dashboard`
|
|
|
+- `GET /agents`
|
|
|
+- `POST /agents`
|
|
|
+- `POST /agents/status`
|
|
|
+- `POST /agents/escalation`
|
|
|
+- `GET /sla/configs`
|
|
|
+- `POST /sla/configs`
|
|
|
+- `GET /sla/live-status`
|
|
|
+
|
|
|
+## Prioridade 3 — configurações e módulos acessórios
|
|
|
+
|
|
|
+- `GET /settings/overview`
|
|
|
+- `GET /billing/subscription`
|
|
|
+- `GET /billing/plans`
|
|
|
+- `POST /billing/change-plan`
|
|
|
+- `POST /billing/cancel`
|
|
|
+- `GET /support/faq`
|
|
|
+- `GET /support/channels`
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 6. Padrão de implementação recomendado
|
|
|
+
|
|
|
+## Envelope de resposta
|
|
|
+
|
|
|
+Manter o padrão já existente no backend:
|
|
|
+
|
|
|
+```json
|
|
|
+{
|
|
|
+ "status": "ok|failed",
|
|
|
+ "msg": "mensagem",
|
|
|
+ "code": "S_OK|E_VALIDATE|...",
|
|
|
+ "data": {}
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+## Autorização
|
|
|
+
|
|
|
+Todas as rotas privadas devem exigir:
|
|
|
+
|
|
|
+```http
|
|
|
+Authorization: Bearer <jwt>
|
|
|
+```
|
|
|
+
|
|
|
+## Convenção prática para o frontend
|
|
|
+
|
|
|
+Como o frontend atual está em Svelte e usa muitos objetos de tela prontos, o caminho mais simples é:
|
|
|
+
|
|
|
+- backend responder com objetos **já próximos do formato consumido pelas telas**;
|
|
|
+- frontend fazer apenas adaptação leve, se necessário.
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 7. Observações importantes da análise
|
|
|
+
|
|
|
+- O frontend **não está chamando API nenhuma hoje**.
|
|
|
+- Portanto, além de criar as rotas do backend, será necessário integrar o frontend com:
|
|
|
+ - `fetch`/client HTTP;
|
|
|
+ - persistência de token;
|
|
|
+ - carregamento inicial com `POST /user/get`;
|
|
|
+ - tratamento de `401`.
|
|
|
+- Como o frontend usa `adapter-static`, o consumo da API será **client-side**.
|
|
|
+- Isso significa que o backend provavelmente precisará de:
|
|
|
+ - **CORS habilitado**;
|
|
|
+ - URL pública configurável por ambiente.
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 8. Resumo executivo
|
|
|
+
|
|
|
+## O que já existe
|
|
|
+
|
|
|
+- frontend pronto visualmente;
|
|
|
+- banco PostgreSQL bem modelado para analytics/atendimento;
|
|
|
+- login e registro no backend;
|
|
|
+- JWT funcionando;
|
|
|
+- migration pronta.
|
|
|
+
|
|
|
+## O que falta
|
|
|
+
|
|
|
+- praticamente toda a API de produto;
|
|
|
+- integração real do frontend com backend;
|
|
|
+- endpoints agregadores para dashboards;
|
|
|
+- CRUD de agentes;
|
|
|
+- endpoints de SLA;
|
|
|
+- endpoint de perfil;
|
|
|
+- eventualmente modelagem extra para billing/settings.
|
|
|
+
|
|
|
+## Melhor ordem de implementação
|
|
|
+
|
|
|
+1. autenticação real no frontend;
|
|
|
+2. `/user/get`;
|
|
|
+3. dashboard principal;
|
|
|
+4. interações;
|
|
|
+5. analytics/sentiment;
|
|
|
+6. agentes;
|
|
|
+7. SLA;
|
|
|
+8. personas/evolução/executivo;
|
|
|
+9. settings/billing/help.
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 9. Conclusão
|
|
|
+
|
|
|
+O projeto está em um ponto muito bom para integração:
|
|
|
+
|
|
|
+- o frontend já mostra claramente o produto final;
|
|
|
+- o banco já cobre boa parte do domínio;
|
|
|
+- o backend já tem base de auth pronta.
|
|
|
+
|
|
|
+O principal trabalho agora é transformar os mocks do frontend em contratos reais de API.
|
|
|
+
|
|
|
+Este arquivo pode ser usado como backlog técnico para construir o backend na ordem correta.
|