diff --git a/apps/desktop/src/components/DeactivationScreen.tsx b/apps/desktop/src/components/DeactivationScreen.tsx index 972a0ea..e237050 100644 --- a/apps/desktop/src/components/DeactivationScreen.tsx +++ b/apps/desktop/src/components/DeactivationScreen.tsx @@ -1,6 +1,24 @@ -import { ShieldAlert, Mail } from "lucide-react" +import { ShieldAlert, Mail, RefreshCw } from "lucide-react" +import { useState } from "react" + +type DeactivationScreenProps = { + companyName?: string | null + onRetry?: () => Promise | void +} + +export function DeactivationScreen({ companyName, onRetry }: DeactivationScreenProps) { + const [isRetrying, setIsRetrying] = useState(false) + + const handleRetry = async () => { + if (isRetrying || !onRetry) return + setIsRetrying(true) + try { + await onRetry() + } finally { + setIsRetrying(false) + } + } -export function DeactivationScreen({ companyName }: { companyName?: string | null }) { return (
@@ -8,9 +26,9 @@ export function DeactivationScreen({ companyName }: { companyName?: string | nul Acesso bloqueado -

Dispositivo desativada

+

Dispositivo desativado

- Esta dispositivo foi desativada temporariamente pelos administradores. Enquanto isso, o acesso ao portal e o + Este dispositivo foi desativado temporariamente pelos administradores. Enquanto isso, o acesso ao portal e o envio de informações ficam indisponíveis.

{companyName ? ( @@ -29,12 +47,25 @@ export function DeactivationScreen({ companyName }: { companyName?: string | nul
- - Falar com o suporte - +
+ + Falar com o suporte + + {onRetry && ( + + )} +
diff --git a/apps/desktop/src/components/MachineStateMonitor.tsx b/apps/desktop/src/components/MachineStateMonitor.tsx index c3f220a..69b8b80 100644 --- a/apps/desktop/src/components/MachineStateMonitor.tsx +++ b/apps/desktop/src/components/MachineStateMonitor.tsx @@ -18,9 +18,10 @@ type MachineStateMonitorProps = { machineId: string onDeactivated?: () => void onTokenRevoked?: () => void + onReactivated?: () => void } -function MachineStateMonitorInner({ machineId, onDeactivated, onTokenRevoked }: MachineStateMonitorProps) { +function MachineStateMonitorInner({ machineId, onDeactivated, onTokenRevoked, onReactivated }: MachineStateMonitorProps) { const machineState = useQuery(api.machines.getMachineState, { machineId: machineId as Id<"machines">, }) @@ -65,6 +66,12 @@ function MachineStateMonitorInner({ machineId, onDeactivated, onTokenRevoked }: onDeactivated?.() } + // Detecta mudança de inativo para ativo (reativação) + if (previousIsActive.current === false && machineState.isActive === true) { + console.log("[MachineStateMonitor] Máquina foi reativada") + onReactivated?.() + } + // Detecta mudança de token válido para inválido if (previousHasValidToken.current === true && machineState.hasValidToken === false) { console.log("[MachineStateMonitor] Token foi revogado (reset)") @@ -74,7 +81,7 @@ function MachineStateMonitorInner({ machineId, onDeactivated, onTokenRevoked }: // Atualiza refs previousIsActive.current = machineState.isActive previousHasValidToken.current = machineState.hasValidToken - }, [machineState, onDeactivated, onTokenRevoked]) + }, [machineState, onDeactivated, onTokenRevoked, onReactivated]) // Este componente nao renderiza nada return null diff --git a/apps/desktop/src/main.tsx b/apps/desktop/src/main.tsx index 7979f0e..c1aa4b8 100644 --- a/apps/desktop/src/main.tsx +++ b/apps/desktop/src/main.tsx @@ -12,6 +12,8 @@ import { cn } from "./lib/utils" import { ChatApp } from "./chat" import { DeactivationScreen } from "./components/DeactivationScreen" import { MachineStateMonitor } from "./components/MachineStateMonitor" +import { api } from "./convex/_generated/api" +import type { Id } from "./convex/_generated/dataModel" import type { SessionStartedEvent, UnreadUpdateEvent, NewMessageEvent, SessionEndedEvent } from "./chat/types" // URL do Convex para subscription em tempo real @@ -724,12 +726,36 @@ useEffect(() => { } }, [token]) // eslint-disable-line react-hooks/exhaustive-deps -// Callbacks para quando a máquina for desativada ou resetada +// Callbacks para quando a máquina for desativada, resetada ou reativada const handleMachineDeactivated = useCallback(() => { console.log("[App] Máquina foi desativada - mostrando tela de bloqueio") setIsMachineActive(false) }, []) +const handleMachineReactivated = useCallback(() => { + console.log("[App] Máquina foi reativada - liberando acesso") + setIsMachineActive(true) +}, []) + +// Callback para o botão "Verificar novamente" na tela de desativação +// Usa o convexClient diretamente para fazer uma query manual +const handleRetryCheck = useCallback(async () => { + if (!convexClient || !config?.machineId) return + console.log("[App] Verificando estado da máquina manualmente...") + try { + const state = await convexClient.query(api.machines.getMachineState, { + machineId: config.machineId as Id<"machines">, + }) + console.log("[App] Estado da máquina:", state) + if (state?.isActive) { + console.log("[App] Máquina ativa - liberando acesso") + setIsMachineActive(true) + } + } catch (err) { + console.error("[App] Erro ao verificar estado:", err) + } +}, [convexClient, config?.machineId]) + const handleTokenRevoked = useCallback(async () => { console.log("[App] Token foi revogado - voltando para tela de registro") if (store) { @@ -1582,20 +1608,31 @@ const resolvedAppUrl = useMemo(() => { ) } + // Monitor sempre ativo quando há token e machineId + const machineMonitor = token && config?.machineId && convexClient ? ( + + ) : null + + // Tela de desativação (renderizada separadamente para evitar container com fundo claro) + if (token && !isMachineActive) { + return ( + <> + {machineMonitor} + + + ) + } + return (
{/* Monitor de estado da maquina em tempo real via Convex */} - {token && config?.machineId && convexClient && ( - - )} - {token && !isMachineActive ? ( - - ) : ( + {machineMonitor}
{
)}
- )} -
) } diff --git a/src/components/automations/automation-runs-dialog.tsx b/src/components/automations/automation-runs-dialog.tsx index 0e507e0..9582040 100644 --- a/src/components/automations/automation-runs-dialog.tsx +++ b/src/components/automations/automation-runs-dialog.tsx @@ -61,16 +61,16 @@ const ACTION_TYPE_LABELS: Record = SEND_EMAIL: { label: "Enviar e-mail", icon: Mail }, SET_PRIORITY: { label: "Alterar prioridade", icon: ArrowRight }, MOVE_QUEUE: { label: "Mover para fila", icon: ArrowRight }, - ASSIGN_TO: { label: "Atribuir responsavel", icon: UserCheck }, - ADD_INTERNAL_COMMENT: { label: "Comentario interno", icon: MessageSquare }, + ASSIGN_TO: { label: "Atribuir responsável", icon: UserCheck }, + ADD_INTERNAL_COMMENT: { label: "Comentário interno", icon: MessageSquare }, APPLY_CHECKLIST_TEMPLATE: { label: "Aplicar checklist", icon: ListChecks }, SET_CHAT_ENABLED: { label: "Chat habilitado", icon: ToggleRight }, - SET_FORM_TEMPLATE: { label: "Aplicar formulario", icon: FileText }, + SET_FORM_TEMPLATE: { label: "Aplicar formulário", icon: FileText }, } const PRIORITY_LABELS: Record = { LOW: "Baixa", - MEDIUM: "Media", + MEDIUM: "Média", HIGH: "Alta", URGENT: "Urgente", } @@ -93,7 +93,7 @@ function ActionDetails({ action }: { action: ActionApplied }) {
{details.recipients && Array.isArray(details.recipients) && (
- Destinatarios: + Destinatários:
{(details.recipients as string[]).map((email, i) => ( @@ -147,14 +147,14 @@ function ActionDetails({ action }: { action: ActionApplied }) { {action.type === "ASSIGN_TO" && (
- Responsavel:{" "} + Responsável:{" "} {String(details.assigneeName ?? "—")}
)} {action.type === "SET_FORM_TEMPLATE" && (
- Formulario:{" "} + Formulário:{" "} {String(details.formTemplateLabel ?? details.formTemplate ?? "—")}
)} @@ -170,7 +170,7 @@ function ActionDetails({ action }: { action: ActionApplied }) { {action.type === "ADD_INTERNAL_COMMENT" && (
- Comentario interno adicionado ao ticket + Comentário interno adicionado ao ticket
)} @@ -199,7 +199,7 @@ function ExpandedDetails({ run }: { run: AutomationRunRow }) { {run.ticket ? (
- Referencia:{" "} + Referência:{" "} #{run.ticket.reference}
@@ -208,22 +208,22 @@ function ExpandedDetails({ run }: { run: AutomationRunRow }) {
) : ( -

Ticket nao disponivel

+

Ticket não disponível

)}
- {/* Info da Execucao */} + {/* Info da Execução */}
-

Execucao

+

Execução

Evento:{" "} {EVENT_LABELS[run.eventType] ?? run.eventType}
- Condicoes:{" "} + Condições:{" "} - {run.matched ? "Atendidas" : "Nao atendidas"} + {run.matched ? "Atendidas" : "Não atendidas"}
{run.error && ( @@ -236,11 +236,11 @@ function ExpandedDetails({ run }: { run: AutomationRunRow }) {
- {/* Acoes Aplicadas */} + {/* Ações Aplicadas */} {hasActions && (

- Acoes aplicadas ({run.actionsApplied!.length}) + Ações aplicadas ({run.actionsApplied!.length})

{run.actionsApplied!.map((action, i) => ( @@ -252,7 +252,7 @@ function ExpandedDetails({ run }: { run: AutomationRunRow }) { {!hasActions && run.status === "SUCCESS" && (
-

Nenhuma acao foi aplicada nesta execucao.

+

Nenhuma ação foi aplicada nesta execução.

)}
@@ -389,7 +389,7 @@ export function AutomationRunsDialog({ const eventLabel = EVENT_LABELS[run.eventType] ?? run.eventType const actionsCount = run.actionsApplied?.length ?? 0 const actionsLabel = - actionsCount === 1 ? "Aplicou 1 acao" : `Aplicou ${actionsCount} acoes` + actionsCount === 1 ? "Aplicou 1 ação" : `Aplicou ${actionsCount} ações` const createdAtLabel = formatDateTime(run.createdAt) const details = run.status === "ERROR" @@ -397,10 +397,10 @@ export function AutomationRunsDialog({ : run.status === "SKIPPED" ? run.matched ? "Ignorada" - : "Condicoes nao atendidas" + : "Condições não atendidas" : actionsCount > 0 ? actionsLabel - : "Sem alteracoes" + : "Sem alterações" const isExpanded = expandedId === run.id const toggleExpand = () => setExpandedId(isExpanded ? null : run.id)