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 /loginPOST /registerMas o frontend já possui diversas telas prontas. Portanto, este arquivo serve como mapa de implementação do backend, indicando:
Pasta: nettown_frontend
Stack identificada:
SvelteKitadapter-staticSvelte 5Tailwindlayerchartlucide-sveltefetch, axios, Authorization ou persistência de JWT implementados nas telas principais.admin@nettown.comadminsrc/lib/core/models/mock-data.jssrc/lib/features/sentiment/data/sentiment-dashboard.mock.jsprofile e perfilsubscription e assinaturahelp e ajudaO frontend está pronto como produto visual e funcional local, mas não está integrado ao backend.
Ou seja:
Pasta: php_api
Stack identificada:
PHPFrameworkXFirebase JWTPostgreSQLPDOPOST /loginPOST /registerGET /jwthelloworld protegido por JWTO backend hoje responde com envelope padrão:
{
"status": "ok",
"msg": "[100] Request ok.",
"code": "S_OK",
"data": {}
}
Em erro:
{
"status": "failed",
"msg": "mensagem de erro",
"code": "E_VALIDATE",
"data": {}
}
email + password.subemailcompany_idroleuser_deleted_at = 'infinity'.O backend já está preparado para:
Mas a camada de API de produto ainda não foi construída.
companyuseroperatoroperator_channelsla_configintegrationclientconversationmessagemessage_attachmentmessage_reactionconversation_participantwebhook_eventconversation_analysisaspect_feedbackemotion_snapshotpublic_opinionalertai_actionpersonaclient_personabest_actionvolume_snapshotsentiment_evolutionplaybooks_monitoroperator_daily_statskpi_snapshotEstas são as principais lacunas identificadas:
Filtro de unidade
unidade em várias telas.Configuração de alerta de SLA por percentual
alertPct.sla_config hoje guarda apenas:sla_config_response_hourssla_config_resolution_hoursConfigurações / notificações
Assinatura / billing
Ajuda / FAQ / suporte
/
/login
POST /login/dashboard/dashboard/interactions/dashboard/analytics/dashboard/personas/dashboard/evolucao/dashboard/executive/dashboard/agents/dashboard/sla/dashboard/settings/dashboard/profile/dashboard/subscription/dashboard/helpRotas duplicadas equivalentes:
/dashboard/perfil/dashboard/assinatura/dashboard/ajudaGET /me;POST /loginGET /mePOST /loginRequest:
{
"email": "admin@empresa.com",
"password": "12345678"
}
Response:
{
"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 /meObjetivo:
Response:
{
"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://..."
}
}
}
/dashboard)Hoje a tela usa mocks para:
GET /dashboard/overviewQuery params sugeridos:
period=today|yesterday|weekunit=all|flagship|franquias|pop-up|digitalarea=all|atendimento|produto|logistica|marketingsentiment=all|positive|neutral|negativevolume_view=hour|day{
"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": []
}
}
}
}
kpi_snapshotalertconversationclientoperatoremotion_snapshotvolume_snapshotconversation_analysis/dashboard/interactions)Hoje a tela mostra:
GET /interactionsGET /interactions/detailsGET /interactionsQuery params sugeridos:
pageper_pagesearchfilter=all|my_clients|new|unfinishedsentimentoperator_idcompany_idResponse:
{
"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/detailsQuery params sugeridos:
conversation_idResponse:
{
"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
}
}
}
conversationmessageconversation_analysisclientoperator/dashboard/analytics)Hoje a tela trabalha com um view model único contendo:
GET /analytics/sentiment/dashboardQuery params sugeridos:
timeframe=day|week|monthcompany_idaspectsentimentResponse:
{
"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": []
}
]
}
}
alertpublic_opinionaspect_feedbackconversation_analysisclientconversation/dashboard/personas)A tela precisa de:
GET /personas/overviewQuery params sugeridos:
period=week|month|quarterunitareasentiment=all|positive|neutral|negativeResponse:
{
"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"
}
]
}
}
personaclient_personabest_actionconversation_analysismessage/dashboard/evolucao)A tela precisa de:
GET /evolution/overviewResponse:
{
"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
}
}
}
sentiment_evolutionplaybooks_monitorkpi_snapshot/dashboard/executive)A tela precisa de:
GET /executive/dashboardResponse:
{
"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%"
}
}
}
kpi_snapshotemotion_snapshotoperator_daily_statsalertsla_config/dashboard/agents)A tela precisa de:
GET /agentsPOST /agentsPOST /agents/statusPOST /agents/escalationGET /agentsResponse:
{
"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 /agentsRequest:
{
"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.
operatoroperator_channeloperator_daily_stats/dashboard/sla)A tela precisa de:
GET /sla/configsPOST /sla/configsGET /sla/live-statusGET /sla/configsResponse:
{
"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.
O banco atual não possui alertPct na tabela sla_config.
Então existem duas opções:
sla_config;sla_configconversationoperator/dashboard/settings)A tela é estática e mostra:
GET /settings/overviewResponse:
{
"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"
}
]
}
}
Esta parte do produto não está coberta pelo schema atual de forma completa.
Existe suporte parcial em:
integrationoperator_channelMas não há modelagem explícita para preferências de notificação e configurações gerais.
/dashboard/profile e /dashboard/perfil)POST /me/change-passwordPOST /me/change-passwordRequest:
{
"currentPassword": "senha-atual",
"newPassword": "senha-nova",
"confirmPassword": "senha-nova"
}
usercompany/dashboard/help e /dashboard/ajuda)Opcionalmente:
GET /support/faqGET /support/channelsEssa tela pode continuar 100% estática no frontend, se preferir.
POST /login XPOST /me/change-passwordGET /dashboard/overviewGET /interactionsGET /interactions/detailsGET /analytics/sentiment/dashboardGET /personas/overviewGET /evolution/overviewGET /executive/dashboardGET /agentsPOST /agentsPOST /agents/statusPOST /agents/escalationGET /sla/configsPOST /sla/configsGET /sla/live-statusGET /settings/overviewGET /billing/subscriptionGET /billing/plansPOST /billing/change-planPOST /billing/cancelGET /support/faqGET /support/channelsManter o padrão já existente no backend:
{
"status": "ok|failed",
"msg": "mensagem",
"code": "S_OK|E_VALIDATE|...",
"data": {}
}
Todas as rotas privadas devem exigir:
Authorization: Bearer <jwt>
Como o frontend atual está em Svelte e usa muitos objetos de tela prontos, o caminho mais simples é:
fetch/client HTTP;401.adapter-static, o consumo da API será client-side.O projeto está em um ponto muito bom para integração:
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.