--- name: Sessão 24/04 - WhatsApp + Social + Notifier + Remote Desktop description: Entregas grandes desta sessão longa; referência rápida pra não repetir trabalho type: project originSessionId: 717f106c-cb9a-4b0e-bbff-97d35eddf122 --- # Sessão 24/04/2026 — Entregas ## WhatsApp Chat (intranet) - Aguardando no topo da sidebar + destaque pulsante laranja - Fix contatos sumindo (regra `last_attended_by`) - Auto-atribuição ao responder (resolve caso SS Qualitá aparecer como "aguardando") - Responder / Editar / Apagar (pra mim + pra todos) via **micro-server 8001** (aditivo, não toca TSeD 8000) - Endpoints 8001: `/message/:key/delete`, `/edit`, `/reply`, `/presence`, `/read-messages`, `/find-node`, `/list-all-devices`, `/presence-subscribe` - `assertSessions(jid, true)` antes de delete/edit (resolve `Bad MAC` Signal) — revoke passou a chegar de fato - Edit in-place no textarea, preserva assinatura `*Nome:* ` via regex - Edit só enquanto cliente não respondeu - Menu restrito: **admin** vê todas opções; **Erick/Valeria** só **Editar** - Botões header (editar contato / limpar / apagar conversa) gated por `$isAdmin` (não por perm, já que fallback antigo causava falso-positivo) - Digitando... + check azul (read receipts) funcionando - Layout WhatsApp chat ocupa 100% janela (override `body[data-layout=horizontal] .page-content { margin-top:50px; padding:0; }`) - SweetAlert2 adicionado ao view do chat (era o que quebrava edit/apagar no menu) ## Social Media (intranet + portal cliente) - Endpoint `/api/notificacoes` agora retorna `social` (posts rejeitados + cronogramas rejeitados) - Notifier v1.8.1 processa esse novo tipo - Portal cliente: novo menu **"Conectar Redes"** em Redes Sociais (só aparece se cliente tem registro em `social_clientes`) - Controller: `AreaCliente::redesSociais` / `redesSociaisConectar` - IMPORTANTE: `social_contas.cliente_id` aponta para `social_clientes.id`, NÃO `clientes.id` — fazer JOIN - View `area_cliente/redes_sociais.php` (cards Facebook/Instagram/etc, status real pelo `access_token`, não só pelo status do banco) - Briefing EDITÁVEL no modal admin "Inserir conteúdo para publicação" (`social_media/index.php` — textarea `epPlanejamento`, envio já tratado em `salvarOuEnviar`) - Regra de alteração pelo cliente: - **Planejamento (cronograma)**: só solicita alteração se `status='enviado'`. Aprovado fecha. - **Post individual**: só solicita alteração se `aprovacao_status != 'aprovado'` E `status NOT IN (publicado, publicando)`. Cronograma aprovado NÃO bloqueia (cronograma só aprova tema/data, conteúdo do post ainda vive) - `rejeitarCronograma` agora insere comentário em TODOS os posts pendentes do cronograma + reabre demandas vinculadas - Validação condicional em `postStore`/`postUpdate`: - `planejamento` SEMPRE obrigatório - `conteudo` + `hashtags` obrigatórios APENAS quando `acao='aprovacao'` ou `'publicar'` (fluxo 2 etapas respeitado) - Cron `/cron/social-media/lembrete-aprovacao` funciona de hora em hora; auto-aprovação após 4h ## Notifier Desktop (v1.8.1) - AdminPasswordDialog: força foco via ctypes, aceita admin flexível (nivel=1 OU is_admin/admin flags) - "Configurações" aparece pra todos no tray — dialog pede email+senha de qualquer admin - Item tray só aparece se `nivel_acesso_id=1`? NÃO — corrigi, aparece pra todos; dialog valida - Scheduled Task Windows auto-instalada (XML via schtasks) — restart em até 2min + ao logon - Watchdog VBS + ScheduledTask = dupla proteção contra Task Manager kill - Heartbeat envia `hostname` → backend chama `http://208.110.85.226:8002/find-node` → auto-sync do `mesh_node_id` no banco (a cada 15min) ## Remote Desktop (MeshCentral) - Problema original: banco apontava pro node offline antigo, máquina real estava em outro node - Criei coluna `api_heartbeats.mesh_sync_at` para controle de re-sincronização - Botão "Reconectar" + "Nova aba" no modal do admin - Removido `viewonly=1` do iframe — permite controlar ## Bug crítico desta sessão (resolvido) - SELECT `mesh_sync_at` em `Api::ping` quebrava requests quando coluna ainda não existia - Fix: `ALTER TABLE api_heartbeats ADD COLUMN IF NOT EXISTS mesh_sync_at DATETIME NULL` aplicado no banco prod ## Permissões finais Erick (id=8) / Valeria (id=7) - `wa_acessar_chat, wa_enviar_mensagem, wa_gerenciar_contatos, wa_transferir_conversa, wa_ver_conversas, wa_ver_historico, wa_ver_sem_dono` - NÃO têm: `wa_editar_contato, wa_limpar_chat, wa_apagar_conversa, wa_excluir_contato, wa_admin` ## Limpeza feita - Tabela `cliente_devices` DROPADA (erro meu — entendi "rede" como computadores, era redes sociais) - `ClienteDevice.php` + view + rotas removidos (local + servidor) - `writable/_tmp/*`, `dist_onedir/`, `build/` removidos localmente - `/tmp/*.php/*.py/*.js` de staging deploy removidos (servidores) - Todos os `*.tmp` em `app/` do servidor removidos ## Bug noturno 25/04 (resolvido) - **ParseError no SocialMedia.php e AreaClienteSocial.php** causado por logs de debug com sintaxe mal formada (colchetes e aspas dentro de `log_message` com `array_map` + função anônima). Linha 2340 do SocialMedia e 372 do AreaClienteSocial. - Consequência: durante ~30min endpoints de social retornavam HTTP 500. Usuário reclamou de "misturando tudo" / "salvando errado" — na verdade a aplicação nem processava o request. - `php -l` local não pegou porque o arquivo local foi corrigido antes de confirmar. Aplicativo no servidor teve OPcache travado com versão quebrada entre deploys. - Fix: remover logs complexos com funções anônimas dentro de string interpolada; usar logs simples como `log_message('info', "[X] k={$val}")`. - Lição: `log_message` com `array_map(function($p){return [...]}, $posts)` dentro de string interpolada + JSON_UNESCAPED_UNICODE quebra o PHP parser em versões antigas. Fazer `$log = json_encode(...); log_message('info', "[X] $log")` separado. ## Fluxo 2 etapas social (regra final) - `postStore`/`postUpdate`: `planejamento` sempre obrigatório. `conteudo` + `hashtags` obrigatórios APENAS quando `acao='aprovacao'` ou `'publicar'`. - NÃO copiar automaticamente planejamento → conteudo (isso misturava campos e confundia admin). Cada campo independente. - `rejeitar($postId)` só bloqueia se `aprovacao_status='aprovado'` OR `status IN (publicado, publicando)`. Cronograma aprovado NÃO bloqueia alteração de post individual. - `rejeitarCronograma($id)` só permite se `status='enviado'`. Aprovado = fechado. - Briefing editável no modal admin "Inserir conteúdo para publicação" (textarea `epPlanejamento`).