22 KiB
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 emplano.md.Formato: uma entrada por rodada,
### Rodada N — Título; quando a rodada não tem número registrado,### Títulosó. A numeração de rodada não é global — cada aplicação conta as próprias, e o mesmo número designa trabalhos diferentes em arquivos diferentes. Ao citar uma rodada, sempre nomear o arquivo. Ver o topo deplano.md.
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 aUsuario— vinculada, os dados vêm sempre do cadastro; avulsa,nome/departamento/numeromoram na própria linha) eRamalAusencia(esta_ativa()calcula "ausente agora" comparando datas/horas, sem armazenar um flag;encerrada_manualmentepermite encerrar antes do previsto sem mexer no período original). - Mesmo padrão visualizar/editar da rodada 19 (Links & Ferramentas):
ramaisganhou os dois apps na árvore de permissões, com o mesmo cuidado noseed_portal.pyde forçareditar=Falsepra todo perfil que não seja Integração e Inovação (já queramaisestá emBASE_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 agerencia_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*deperfis-acesso.css(mesmo padrão queusuarios.htmljá usa sem CSS de tabela próprio). Nos outros 5 templates, o item de menu "Ramais" (anteshref="#") e o botão "Acessar Ramais" do topbar (antes um<button>sem handler nenhum) passaram a apontar de verdade praramais.html. - Migração
0012_ramal_ramalausenciagerada e aplicada no ambiente local;seed_portalreaplicado (confirmado via shell: 7 perfis comramais.apps.editar=False, só "Integração e Inovação" comTrue).
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:
- "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
Ramalvinculada a umUsuariopra 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. - "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:
Ramalperdeu o campousuario(migração0013_remove_ramal_usuario_alter_ramal_nome) — agora é só linha avulsa (sem conta de sistema por trás, ex.: telefone de sala), comnomeobrigatório edepartamento/numeroopcionais (podem ficar pendentes, igual a um colaborador de verdade).RamalViewSet.list()foi reescrito pra mesclar duas fontes numa lista só, sem passar peloRamalSerializer: todoUsuarioativo (linha montada direto do cadastro —nome,departamentos,ramal) + as linhas avulsas deRamal. Cada item ganha umidsintético ("usuario-<id>"/"avulso-<id>") e umtipo, 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 emUsuario.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>(viaGET /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()filtrais_active=True) — efeito colateral da reescrita que corrige uma limitação que a rodada 21 tinha deixado em aberto. - Select/textarea estilizados:
components.cssganhou.modal-field select/.modal-field textareagenéricos (seta customizada viabackground-image, sem oappearancenativo 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:
- Overflow no modal de Adicionar Ramal:
.modal-field-rowusavagrid-template-columns: 1fr 1frsemminmax(0, ...), e os inputs de.modal-fieldnão tinhamwidth: 100%explícito — o conteúdo (tamanho intrínseco do<input>) empurrava a grade além da borda do modal. Corrigido emcomponents.css(grid-template-columns: minmax(0, 1fr) minmax(0, 1fr)+width: 100%eminput/select/textareade.modal-field) — bug genérico, vale pra qualquer modal do portal com campos lado a lado, não só Ramais. - 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 emramais.css:.ram-chamado-cardpassou 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 virouflex:1dentro do.modal-card(que já é flex-column), preenchendo todo o espaço vertical restante. - 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
RamalAusenciaViewSetque já existia (nenhuma mudança de backend —GET/PATCH/DELETEem/api/ramais-ausencias/{id}/já funcionavam, só faltava a UI): o badge virou um<button data-ram-ver-ausencia>que busca o registro viaGETe 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ó marcavaencerrada_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-btnvoltou 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 telaramais.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, reaproveitandoGET /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 tiverapps.visualizaremramais— 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-tabledeperfis-acesso.cssporqueportal.html/links-ferramentas.html/calendario-individual.htmlnão carregam esse CSS (sóramais.html/usuarios.html/perfis-acesso.htmlcarregam) —ramais-lookup.cssficou 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 deRamalavulso):TelefoneExterno(nome/ramal/telefone/observações, sónomeobrigatório) eFuncaoTelefonia(comando/função/resumo, sócomandoobrigató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 campoordemmanual e endpoint de reorder. - Permissão reaproveitada: as duas subtelas usam o mesmo par
ramais.apps.visualizar/ramais.apps.editarque já existia (PermissaoApp("ramais", app_key)) — são subtelas da mesma seção do menu, não aplicações novas; nenhuma mudança emcatalogo.py. - Seed só para Funções de Telefonia:
seed_portal.pyganhouFUNCOES_TELEFONIA_SEED(as 13 linhas do print, viaupdate_or_createporcomando, 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-panelque já existia emperfis-acesso.css(mesmo padrão das abas Permissões/Usuários deperfis-acesso.html), com uma implementação independente emramais.js(data-ram-tab/data-ram-tab-panel/activeRamTab) pra não colidir comprofiles.js. O conteúdo que já existia (filtros/tabela/botões de Ramais) migrou pro paineldata-ram-tab-panel="ramais", sem mudar de comportamento. - Endpoints novos:
/api/telefones-externos/e/api/funcoes-telefonia/, CRUD padrão viaModelViewSet.
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
ModelViewSetrelacionados (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"—RamalAusenciaViewSetusa as mesmas chaves deramais-visualizar/ramais-editardo diretório, por ser parte dessa mesma aba. seed_portal.py: o override que forçaeditar=Falsepros 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 únicocanView/canManagepra 5 pares (um por aba), um objetoramTabViewPermsque esconde (hidden) o botão de cada aba semvisualizar, 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 deapps.visualizargenérico praapps["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).
Rodada 33 — Revisão de interface de 30/09/2026: Ramais e consulta rápida
Correções vindas da revisão de interface de 30/09/2026 (ver plano.md), só no frontend:
- Carga por permissão:
ramais.jssó busca a lista de cada aba que o usuário pode visualizar (antes buscava as três e gerava 403 nas abas sem permissão). Falha de carga mostra "Não foi possível carregar. Recarregue a página; se persistir, contate a Inovação." na própria tabela. - Lista vazia x filtro: Ramais e Telefones Externos diferenciam "Nenhum ... cadastrado ainda." de "Nenhum resultado para o filtro.".
- Envio duplo: os cinco "Salvar"/"Adicionar" (ramal, telefone externo, função, criar e editar ausência) ficam travados até a resposta (helper
comTrava). Excluir ramal/telefone/função/ausência trava o próprio botão e mostrapidAlertna falha. - Sair sem salvar: Cancelar e clique fora dos cinco formulários (inclusive o modo de edição do popup de ausência e o "Cancelar edição") comparam com o retrato da abertura e pedem confirmação quando houve alteração.
- Ausência: o select de usuário ganhou a opção vazia "Selecione…" (antes o primeiro nome vinha marcado) e a validação aponta e foca o campo que falta.
- Consulta rápida (
ramais-lookup.js): erro de carga não vira mais "Nenhum ramal encontrado." nem fica em cache (a próxima abertura tenta de novo); "Carregando…"; ordenação comlocaleCompare("pt-BR", { numeric: true });overscroll-behavior: containna tabela etabular-numsna coluna Ramal. Botão "Close" virou "Fechar" e o placeholder da busca usa "…" (emlinks-ferramentas.htmlecalendario-individual.html). - Template: placeholders que repetiam o rótulo viraram exemplos ("Ex.: Recepção", "Ex.: 201"...), telefone com
type="tel", ramal cominputmode="numeric", comando de função comspellcheck="false";tabular-numsnas colunas de ramal/telefone.