feat(agents): widget de status na topbar (entrar/pausar/retomar/sair)
Achado real reportado pelo usuário: "não achei como deixar o agente
online, tem algum perfil ou tipo de usuario especifico?" — não era
permissão nenhuma. POST /agents/me/login|pause|resume|logout sempre
funcionaram sem exigir permissão (só JwtAuthGuard, resolvem "meu
agente" pelo userId do próprio token). O problema: nenhuma tela chamava
esses endpoints — só existia o CRUD admin de Agentes (criar o vínculo
usuário+ramal), nunca um controle pro próprio usuário logado.
Adicionado GET /agents/me (404 = usuário sem Agent neste tenant, não um
erro — é assim que o widget decide se aparece) e GET /agents/me/pause-
reasons (motivos de pausa sem exigir agents.view, que listaria TODOS os
agentes do tenant — permissão que o role "agent" nunca teve e não devia
ganhar só pra isso).
AgentStatusWidget na topbar (visível em qualquer página do app do
tenant) só aparece pra quem tem Agent vinculado: Offline -> Entrar ->
Disponível -> Pausar (escolhe motivo) -> Em pausa -> Retomar/Sair.
Achado real construindo o widget: as Server Actions de login/logout/
pause/resume exportadas como arrow function que só repassavam
argumentos quebravam em runtime ("Server Actions must be async
functions") — o mesmo bug já documentado antes nesta sessão (wizard de
campanhas). Corrigido declarando como async function de verdade.
Testado ponta a ponta com Playwright, logado como um agente de teste
real (usuário novo, role "agent", Agent vinculado a um ramal real): o
fluxo completo (entrar/pausar/retomar/sair) funciona pela tela, sem
nenhuma chamada de API manual.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BFaBaBSQGhyXGEgtTYZGV8
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
"use server";
|
||||
|
||||
import { requireSession } from "@/lib/session";
|
||||
import { apiFetch, ApiError } from "@/lib/api";
|
||||
|
||||
function extractErrorMessage(err: unknown): string {
|
||||
if (err instanceof ApiError) {
|
||||
try {
|
||||
const parsed = JSON.parse(err.message);
|
||||
if (Array.isArray(parsed.message)) return parsed.message.join(" ");
|
||||
if (typeof parsed.message === "string") return parsed.message;
|
||||
} catch {
|
||||
// corpo não era JSON
|
||||
}
|
||||
return err.message || "Falha inesperada na API.";
|
||||
}
|
||||
return "Falha inesperada. Tente novamente.";
|
||||
}
|
||||
|
||||
type ActionResult = { ok: true; state: string } | { ok: false; error: string };
|
||||
|
||||
async function callAgentMe(path: string, body?: unknown): Promise<ActionResult> {
|
||||
const session = await requireSession();
|
||||
try {
|
||||
const res = await apiFetch<{ state: string }>(`/agents/me${path}`, session.accessToken, {
|
||||
method: "POST",
|
||||
...(body !== undefined ? { body: JSON.stringify(body) } : {}),
|
||||
});
|
||||
return { ok: true, state: res.state };
|
||||
} catch (err) {
|
||||
return { ok: false, error: extractErrorMessage(err) };
|
||||
}
|
||||
}
|
||||
|
||||
// Server Actions exportadas como arrow function que só repassam
|
||||
// argumentos pra outra função quebram em runtime ("Server Actions must
|
||||
// be async functions") — achado real já documentado nesta sessão
|
||||
// (mesmo bug do wizard de campanhas). Precisam ser `async function` de
|
||||
// verdade, mesmo só repassando.
|
||||
export async function agentLogin(): Promise<ActionResult> {
|
||||
return callAgentMe("/login");
|
||||
}
|
||||
export async function agentLogout(): Promise<ActionResult> {
|
||||
return callAgentMe("/logout");
|
||||
}
|
||||
export async function agentResume(): Promise<ActionResult> {
|
||||
return callAgentMe("/resume");
|
||||
}
|
||||
export async function agentPause(pauseReasonId: string): Promise<ActionResult> {
|
||||
return callAgentMe("/pause", { pauseReasonId });
|
||||
}
|
||||
Reference in New Issue
Block a user