portal_publico/docs/ramais/CHANGELOG.md

20 KiB
Raw Blame History

Changelog — Ramais

Histórico específico desta aplicação, extraído de plano.md. Rodadas que mudaram mais de uma aplicação ao mesmo tempo (favicon, tema, texto do menu) continuam em plano.md — a numeração de rodada é a mesma usada lá, para referência cruzada.

Rodada 21 — Ramais — tela nova

Pedido: reconstruir de verdade a tela "Ramais" (até então só um item de menu com href="#" e um botão "Acessar Ramais" sem destino em todos os shells), com base em prints da ferramenta antiga: diretório de ramais alimentado pelo cadastro real de usuário (Nome/Departamento/Ramal), modal de "Adicionar um Novo Ramal" (com opção de vincular um usuário existente ou criar uma linha avulsa "Não Tem Usuário"), botão "Novo Chamado", botão "Criar Ausência" e cartões especiais para colaborador ausente/aniversariante. Três decisões confirmadas com o usuário antes de implementar:

  • Novo Chamado abre embutido na própria tela (modal com <iframe> apontando pro token da ferramenta de chamados), não em nova aba — diferente do padrão de todo outro link externo do portal.
  • Editar um ramal vinculado a usuário: só o número do Ramal é gravável nesta tela — grava direto em Usuario.ramal; Nome/Departamento continuam só leitura do cadastro.
  • Ausência: "(AUSENTE)" é calculado automaticamente comparando a hora atual com o período criado, mais uma ação de "Encerrar Ausência" pra quem volta antes do previsto.
  • Férias: fora de escopo — o próprio usuário disse que depende de uma integração futura com outro banco; nenhuma UI de férias foi construída.

O que foi construído (detalhe técnico completo em docs/ramais/ramais.md):

  • Dois models novos: Ramal (linha da tela, opcionalmente vinculada a Usuario — vinculada, os dados vêm sempre do cadastro; avulsa, nome/departamento/numero moram na própria linha) e RamalAusencia (esta_ativa() calcula "ausente agora" comparando datas/horas, sem armazenar um flag; encerrada_manualmente permite encerrar antes do previsto sem mexer no período original).
  • Mesmo padrão visualizar/editar da rodada 19 (Links & Ferramentas): ramais ganhou os dois apps na árvore de permissões, com o mesmo cuidado no seed_portal.py de forçar editar=False pra todo perfil que não seja Integração e Inovação (já que ramais está em BASE_KEYS, permissions_from_keys() habilitaria os dois de uma vez sem esse override).
  • Endpoint dedicado GET /api/ramais/usuarios/ pra alimentar o <select> "Lista de Usuários" dos modais, em vez de reaproveitar /api/usuarios/ — aquele é restrito a gerencia_permissoes, e a permissão de Ramais foi mantida deliberadamente desacoplada disso (mesmo espírito da própria refatoração visualizar/editar da rodada 19).
  • Página nova ramais.html + ramais.js + ramais.css, réplica do shell padrão, reaproveitando a tabela genérica .pa-table* de perfis-acesso.css (mesmo padrão que usuarios.html já usa sem CSS de tabela próprio). Nos outros 5 templates, o item de menu "Ramais" (antes href="#") e o botão "Acessar Ramais" do topbar (antes um <button> sem handler nenhum) passaram a apontar de verdade pra ramais.html.
  • Migração 0012_ramal_ramalausencia gerada e aplicada no ambiente local; seed_portal reaplicado (confirmado via shell: 7 perfis com ramais.apps.editar=False, só "Integração e Inovação" com True).

Rodada 22 — Ramais: colaboradores aparecem automaticamente, sem precisar "adicionar"

Depois de testar a rodada 21 no navegador (print anexado: só Gabriel aparecia na lista, porque ele tinha se "adicionado" manualmente pelo modal), o usuário pediu duas coisas:

  1. "Pode trazer automaticamente o cadastro do usuário para esta tela de ramais, ficando apenas pendente o cadastro de ramal, se necessário" — ou seja, o modelo original (rodada 21), que exigia criar uma linha Ramal vinculada a um Usuario pra alguém aparecer no diretório, criava um passo manual desnecessário: todo colaborador com conta no Portal já deveria aparecer sozinho, com o ramal pendente até ser preenchido.
  2. "Melhore a visualização da opção de adicionar ramal, o ícone de seleção está feio e destoa da página" — o <select> nativo do browser (sem nenhum CSS) destoava do tema escuro do resto do portal.

O que mudou:

  • Ramal perdeu o campo usuario (migração 0013_remove_ramal_usuario_alter_ramal_nome) — agora é só linha avulsa (sem conta de sistema por trás, ex.: telefone de sala), com nome obrigatório e departamento/numero opcionais (podem ficar pendentes, igual a um colaborador de verdade).
  • RamalViewSet.list() foi reescrito pra mesclar duas fontes numa lista só, sem passar pelo RamalSerializer: todo Usuario ativo (linha montada direto do cadastro — nome, departamentos, ramal) + as linhas avulsas de Ramal. Cada item ganha um id sintético ("usuario-<id>"/"avulso-<id>") e um tipo, que o frontend usa pra decidir a ação certa.
  • Editar o ramal de um colaborador de verdade deixou de ser "criar uma linha vinculada" — agora é a nova action PATCH /api/ramais/usuarios/{usuario_id}/ (atualizar_ramal_usuario), que grava direto em Usuario.ramal. "Adicionar Ramal" ficou só para linhas avulsas — o <select> "Lista de Usuários" que existia nesse modal foi removido (não faz mais sentido, já que o colaborador já aparece sozinho); só o modal de Criar Ausência ainda tem esse <select> (via GET /api/ramais/usuarios/, agora simplificado pra só id/nome).
  • Excluir só existe pra linhas avulsas agora (não dá pra "excluir" um colaborador do diretório sem excluir a conta dele, o que é escopo da tela de Usuários) — o ícone de lixeira some das linhas de usuário de verdade.
  • Usuário inativo não aparece mais no diretório (list() filtra is_active=True) — efeito colateral da reescrita que corrige uma limitação que a rodada 21 tinha deixado em aberto.
  • Select/textarea estilizados: components.css ganhou .modal-field select/.modal-field textarea genéricos (seta customizada via background-image, sem o appearance nativo do browser) — não existia nenhum estilo pra esses dois elementos antes (só input), então qualquer modal futuro com <select>/<textarea> já sai consistente com o tema escuro sem precisar de CSS próprio.

Pegadinha resolvida durante a migração: como o campo usuario foi removido por completo (não só esvaziado), a única linha Ramal vinculada que já existia (a que o usuário tinha criado testando a rodada 21, ligada à própria conta dele) ficou órfã — virou uma linha avulsa com todos os campos em branco, porque a informação de qual usuário ela representava dependia só da FK removida. Identificada e apagada manualmente depois da migração (não tinha dado nenhum pra preservar, já que os três campos ficavam vazios pra linhas vinculadas no desenho antigo); o ramal do usuário de teste bruno também tinha sido alterado por um smoke test rodado antes desta correção e foi restaurado ao valor original. Sem consequência pra dado real, já que a tela nunca tinha sido usada em produção — mas fica registrado porque é o tipo de coisa que merece atenção se acontecer de novo com dado que importe.

Rodada 23 — Ramais: ajustes visuais + visualizar/editar/excluir ausência

Três pedidos pequenos, feitos em sequência depois de testar a rodada 22 no navegador:

  1. Overflow no modal de Adicionar Ramal: .modal-field-row usava grid-template-columns: 1fr 1fr sem minmax(0, ...), e os inputs de .modal-field não tinham width: 100% explícito — o conteúdo (tamanho intrínseco do <input>) empurrava a grade além da borda do modal. Corrigido em components.css (grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) + width: 100% em input/select/textarea de .modal-field) — bug genérico, vale pra qualquer modal do portal com campos lado a lado, não só Ramais.
  2. Modal "Novo Chamado" cortando conteúdo: a ferramenta externa embutida no iframe (rodada 21) é mais alta que um modal padrão; o iframe tinha altura fixa (min(70vh, 640px)) menor que o conteúdo, cortando o formulário no meio de um campo em vez de rolar de forma previsível. Corrigido em ramais.css: .ram-chamado-card passou a ocupar quase a tela inteira (min(960px, 95vw) × min(92vh, ...), o teto em px ajustado depois pelo próprio usuário direto no CSS) e o iframe virou flex:1 dentro do .modal-card (que já é flex-column), preenchendo todo o espaço vertical restante.
  3. Visualizar/editar/excluir ausência: o usuário passou um print de como devia ficar — clicar no badge "(AUSENTE)" de uma linha abre um modal "Visualizar Ausência" com os campos do período (desabilitados) e dois botões, "Editar Ausência" e "Deletar Ausência". Implementado reaproveitando o RamalAusenciaViewSet que já existia (nenhuma mudança de backend — GET/PATCH/DELETE em /api/ramais-ausencias/{id}/ já funcionavam, só faltava a UI): o badge virou um <button data-ram-ver-ausencia> que busca o registro via GET e abre o modal; "Editar" habilita os mesmos campos in-place (sem modal novo) e troca os botões por "Cancelar"/"Salvar Ausência"; "Deletar" remove o registro de verdade. Isso substituiu o botão "Encerrar Ausência" da rodada 21 (que só marcava encerrada_manualmente=True, preservando o registro) — o campo continua no model, mas sem UI própria; editar/excluir cobre o caso de uso real de forma mais direta, do jeito que foi demonstrado.

Nenhuma migração nova (mudança 3 não tocou o backend). manage.py check limpo depois das mudanças 1 e 2 (não alteraram Python, só confirmado por precaução).

Rodada 24 — Ramais: contraste dos indicadores de ausente/aniversariante

Prints em claro e escuro mostrando as linhas de Bruno (ausente) e Willian (aniversariante) — pedido pra melhorar a legibilidade. Perguntado antes de mexer, porque "observações" no pedido original não batia com nenhum print (nenhum mostrava o campo Observações do modal de Ausência); o usuário confirmou que o problema era o tingimento de fundo da linha inteira (.ram-row--ausente td/.ram-row--aniversario td, rodada 21) somado à cor do próprio selo — duas camadas de cor translúcida da mesma matiz empilhadas, contraste ruim nos dois temas.

Primeira tentativa: removido o tingimento da linha inteira, deixando só o selo comunicar o status (mesmo padrão do selo "Pendente"). O usuário pediu de volta o tingimento — "quero que deixe o tingimento, porém que fique num tom mais forte, para facilitar sua visualização". Desenho final: .ram-row--ausente/.ram-row--aniversario td voltaram (com opacidade maior que a versão original da rodada 21) e .ram-badge--ausente virou um selo (pill) de verdade, igual ao de aniversariante — as duas coisas juntas, cada uma com cor de texto/fundo ajustada por tema via :root[data-theme="light"] (mesmo padrão de override que tokens.css já usa) em vez de reaproveitar --danger/--gold crus, que não tinham contraste suficiente pensados pra texto pequeno sobre um selo ou fundo de linha.

Rodada 30 — "Acessar Ramais" vira modal de consulta rápida, em vez de navegar

Pedido, com print de referência do portal antigo: o botão "Acessar Ramais" do topbar (presente em portal.html, links-ferramentas.html e calendario-individual.html — só essas 3 páginas têm o atalho) não deveria mais levar pra ramais.html; deveria abrir um modal com a lista de ramais cadastrados, com busca por nome/departamento/ramal. O print de referência mostrava um estilo DataTables (branco/azul, "Show N entries", colunas ordenáveis, paginação numerada) — decisão de modernizar a funcionalidade pro visual escuro/roxo do resto do portal, não clonar pixel a pixel (mesmo critério já usado desde a rodada 1).

O que foi construído:

  • #ramais-btn voltou a ser um <button> (não <a href="ramais.html">, que era o estado desde a rodada 21) nas 3 páginas.
  • Modal novo (#ramais-lookup-modal, duplicado nas 3 páginas — mesmo padrão de outros modais compartilhados como o de senha) + static/js/ramais-lookup.js + static/css/ramais-lookup.css: busca numa caixa só (filtra as três colunas ao mesmo tempo, mais simples que a busca em duas caixas da tela ramais.html), ordenação por coluna (clique no cabeçalho alterna asc/desc) e paginação (10/25/50/100 por página) — tudo client-side, sem endpoint novo, reaproveitando GET /api/ramais/ (a mesma listagem mesclada usuário+avulso da tela completa), buscado uma vez por abertura de página.
  • Gate por permissão: o botão some (hidden) se o perfil do usuário não tiver apps.visualizar em ramais — mesmo padrão do resto do app.
  • Botão "Ir para Controle de Ramais" no rodapé do modal é o link de verdade pra ramais.html (tela com edição); o modal em si é só consulta.
  • Pegadinha resolvida: a tabela do modal não podia reaproveitar .pa-table de perfis-acesso.css porque portal.html/links-ferramentas.html/calendario-individual.html não carregam esse CSS (só ramais.html/usuarios.html/perfis-acesso.html carregam) — ramais-lookup.css ficou com estilo de tabela autocontido (.ram-lookup-table*) em vez de depender de um CSS que a página não tem.

Nenhuma mudança de backend — GET /api/ramais/ já existia e já retornava exatamente os dados necessários. manage.py check limpo (confirmado por precaução, já que a mudança foi só front-end).

Rodada 31 — Ramais vira uma seção com 5 subtelas (abas)

Dois prints do sistema antigo mostraram que "Ramais" no fundo é uma seção com 5 subtelas navegáveis por abas: Ramais, Responsável no Tareffa, Telefones Externos, Férias, Funções de Telefonia. Pedido: adicionar essa navegação em ramais.html, construir Telefones Externos e Funções de Telefonia de verdade, e criar Responsável no Tareffa/Férias como abas vazias ("sem informações, pois serão trabalhadas posteriormente"). Isso supera a decisão da rodada 21 de que Férias estava fora de escopo — agora a aba existe (vazia); a limitação de fundo (integração futura com outro banco) continua valendo, só a navegação foi antecipada.

Perguntado antes de implementar se "Funções de Telefonia" (tabela de comandos tipo *01 + Código de Agente → LogOn) deveria ser conteúdo fixo no HTML ou uma lista administrável no banco — o usuário escolheu lista administrável (CRUD completo), mesmo padrão de Telefones Externos.

O que foi construído:

  • Dois models novos, ambos sem FK pra Usuario (dados avulsos, mesmo espírito de Ramal avulso): TelefoneExterno (nome/ramal/telefone/observações, só nome obrigatório) e FuncaoTelefonia (comando/função/resumo, só comando obrigatório). FuncaoTelefonia.Meta.ordering = ["comando"] reproduz sozinho a ordem do print (*0, *01, *02, *03, *1, *2, *20, *21, *22, *23, *5, *503, *8) porque essa é exatamente a ordem lexicográfica da string — dispensou um campo ordem manual e endpoint de reorder.
  • Permissão reaproveitada: as duas subtelas usam o mesmo par ramais.apps.visualizar/ramais.apps.editar que já existia (PermissaoApp("ramais", app_key)) — são subtelas da mesma seção do menu, não aplicações novas; nenhuma mudança em catalogo.py.
  • Seed só para Funções de Telefonia: seed_portal.py ganhou FUNCOES_TELEFONIA_SEED (as 13 linhas do print, via update_or_create por comando, idempotente) — decisão de que são comandos padrão de central telefônica (documentação genérica), diferente de Telefones Externos, que começa vazio de propósito (são contatos reais de fornecedores/terceiros, o usuário cadastra pela própria tela).
  • Abas: reaproveitado o CSS genérico .pa-tabs/.pa-tab/.pa-tab-panel que já existia em perfis-acesso.css (mesmo padrão das abas Permissões/Usuários de perfis-acesso.html), com uma implementação independente em ramais.js (data-ram-tab/data-ram-tab-panel/activeRamTab) pra não colidir com profiles.js. O conteúdo que já existia (filtros/tabela/botões de Ramais) migrou pro painel data-ram-tab-panel="ramais", sem mudar de comportamento.
  • Endpoints novos: /api/telefones-externos/ e /api/funcoes-telefonia/, CRUD padrão via ModelViewSet.

Migração 0014_funcaotelefonia_telefoneexterno gerada e aplicada no ambiente local; seed_portal reexecutado (confirmado via shell: 13 linhas de Funções de Telefonia, na ordem certa). manage.py check limpo. Ainda não testado num navegador de verdade — cadastrar/editar/excluir um Telefone Externo e uma Função de Telefonia, alternar entre as 5 abas, e conferir que um perfil só-visualizar não vê nenhum botão de escrita fica para a próxima validação.

Rodada 32 — Ramais: permissão granular por subtela (visualizar por aba + editar nas 3 administráveis)

No mesmo dia da rodada 31, o usuário pediu um ajuste no modelo de permissão recém-criado: em vez de um único par visualizar/editar cobrindo as 5 abas de Ramais, cada aba deveria ter sua própria permissão de visualização, e só as 3 com conteúdo administrável (Ramais, Telefones Externos, Funções de Telefonia) deveriam ter também uma permissão de edição própria — Responsável no Tareffa/Férias, sendo placeholders vazios, só precisam de visualizar. Pedido explícito de "estado inicial": só "Integração e Inovação" com acesso de editar nas 3, os demais 7 perfis com visualizar liberado em todas as 5.

Implementado reestruturando catalogo.MODULE_APPS["ramais"] de uma lista flat de 2 apps pra 5 subgrupos (mesmo formato {"key", "label", "tools": [...]} já usado em Auditorias) — cada subgrupo é uma aba, com 1 tool (Visualizar) ou 2 (Visualizar/Editar). Isso reaproveitou 100% a árvore de permissões genérica de profiles.js (renderTree/renderEntry/renderLeaf já sabem renderizar subgrupos com tools, usado desde a rodada de Auditorias) — nenhum código de UI novo em Perfis de Acesso.

O que mudou:

  • Backend: os 4 ModelViewSet relacionados (RamalViewSet, RamalAusenciaViewSet, TelefoneExternoViewSet, FuncaoTelefoniaViewSet) passaram a checar a chave específica da própria subtela ("telefones-externos-visualizar"/"editar", etc.) em vez do genérico "visualizar"/"editar" — RamalAusenciaViewSet usa as mesmas chaves de ramais-visualizar/ramais-editar do diretório, por ser parte dessa mesma aba.
  • seed_portal.py: o override que força editar=False pros 7 perfis "normais" cresceu de 1 chave (ramais.apps.editar) pra 3 (ramais-editar, telefones-externos-editar, funcoes-telefonia-editar) — mesmo princípio de sempre, só mais chaves.
  • ramais.js: reescrito o bloco de permissão — de um único canView/canManage pra 5 pares (um por aba), um objeto ramTabViewPerms que esconde (hidden) o botão de cada aba sem visualizar, e a aba ativa por padrão passou a ser a primeira visível (não sempre "Ramais", que pode estar oculta pra um perfil específico no futuro, embora hoje todos os 7 perfis "normais" vejam as 5).
  • ramais-lookup.js (modal de consulta rápida no topbar): trocado de apps.visualizar genérico pra apps["ramais-visualizar"] especificamente, já que esse modal só mostra o diretório de Ramais.

Como isso mudou as chaves armazenadas em PerfilAcesso.permissoes["ramais"]["apps"] (não só valores), foi necessário reexecutar seed_portal pra sincronizar os 8 perfis com o novo formato — sem isso, todo perfil ficaria sem nenhum acesso a Ramais (as chaves antigas visualizar/editar não existem mais no catálogo). Confirmado via shell depois do reseed: os 7 perfis "normais" saíram com as 5 chaves *-visualizar=True e as 3 *-editar=False; só "Integração e Inovação" saiu com as 3 *-editar=True também. gabriel/bruno não foram tocados (proteção da rodada anterior, [[feedback_seed_nao_reseta_gabriel_bruno]] segue valendo). manage.py check limpo.

Nota pra quem for customizar um perfil manualmente na tela de Perfis de Acesso: qualquer ajuste fino que já tivesse sido feito nas chaves antigas (ramais.visualizar/ramais.editar) precisa ser refeito nas novas chaves — é uma troca de namespace, não uma migração automática de valor (JSONField não tem esse mecanismo).