# Changelog — Links & Ferramentas / Acessos Gerais > Histórico específico desta aplicação, extraído de `plano.md`. Rodadas que mudaram mais de uma aplicação ao mesmo tempo (modelo de permissões visualizar/editar em si, ambiente de desenvolvimento) continuam em `plano.md`. > > **Formato**: uma entrada por rodada, `### Rodada N — Título`; quando a rodada não tem número registrado, `### Título` só. **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 de `plano.md`. ### Rodada 16 — Links & Ferramentas — tela nova + permissão de edição dedicada Pedido: estruturar de verdade a tela "Links & Ferramentas" (até então só um item de menu com `href="#"`, sem página própria), com base num print da ferramenta antiga (grade de cartões com logo/nome de sistemas externos — Ottimizza, Sieg, GLPI, Trello, WhatsApp etc., cada um abrindo o link correspondente). Três exigências específicas: 1. **Edição restrita por permissão de perfil**: só perfis com uma permissão dedicada podem reordenar, incluir e remover os cartões — hoje só habilitada para "Integração e Inovação" (`gerencia_links_ferramentas=True` no seed), mas com o toggle já disponível em Perfis de Acesso pra liberar outros perfis depois, sem precisar de código novo. 2. **Adicionar link**: modal com Nome, Link (URL) e "uma foto ou ícone" — interpretado como upload de imagem real (não uma URL de ícone nem um emoji/picker), já que o print de referência mostra logos de marca de cada ferramenta. 3. Nada além disso foi pedido — não existe hoje edição de nome/URL/ícone de um link já criado pela UI (só admin do Django), por decisão de manter o escopo no que foi pedido (adicionar/remover/reordenar). O que foi construído: - **Nova permissão no mesmo padrão de `gerencia_permissoes`**: campo `gerencia_links_ferramentas` (BooleanField) em `PerfilAcesso`, união em `Usuario.gerencia_links_ferramentas()`, classe `PodeGerenciarLinksFerramentas` em `permissions.py`, exposta em `me.gerencia_links_ferramentas`. É uma flag **separada** do toggle de visibilidade do módulo "Links & Ferramentas" (que já existia no catálogo, controla só se o item aparece no menu) — uma controla quem *vê* a tela, a outra quem pode *editar o conteúdo* dela. Adicionado um checkbox dedicado na aba Permissões de `perfis-acesso.html` (`#pa-gerencia-links-ferramentas`), fora da árvore de módulos/apps — hoje é o único toggle "especial" desse tipo com UI própria (`gerencia_permissoes` em si só é setável via seed/admin, sem checkbox equivalente ainda; não alterado nesta rodada por não ter sido pedido). **Esse campo foi revertido na rodada 19** (ver abaixo) em favor do padrão visualizar/editar. - **Model `LinkFerramenta`**: lista **global** (sem FK de usuário, ao contrário de Favorito/WidgetUsuario) — todo mundo vê os mesmos cartões. Campo `ordem` (inteiro) para a sequência de exibição; `icone` é `ImageField` opcional (exigiu adicionar Pillow ao `requirements.txt` e configurar `MEDIA_URL`/`MEDIA_ROOT`, que não existiam no projeto até agora — servidos em `DEBUG` por `config/urls.py`, sem equivalente em produção ainda configurado além do padrão whitenoise/nginx já usado pra `STATIC_ROOT`). - **Reordenação sem endpoint de lote**: mover um cartão faz duas chamadas PATCH trocando o `ordem` de dois itens adjacentes — decisão deliberada de não criar um endpoint de bulk-reorder nem usar drag-and-drop (sem biblioteca no projeto, drag-and-drop nativo teria custo de acessibilidade/touch maior que o ganho); a UI usa duas setas (mover pra cima/baixo) por cartão, visíveis só pra quem tem a permissão. **O drag-and-drop nativo foi adicionado depois** (ver `links-ferramentas-acessos-gerais.md`). - **Upload multipart**: `pidApiRequest` (`api.js`) só sabia enviar JSON — estendido para detectar `body instanceof FormData` e, nesse caso, deixar o browser montar o `Content-Type: multipart/form-data` com boundary sozinho (sem isso, o upload do ícone quebraria). - Página nova `links-ferramentas.html`, réplica do shell padrão (mesma sidebar/topbar dos outros 5 templates) + `links-ferramentas.js` + `links-ferramentas.css`; item do menu "Links & Ferramentas" trocou de `href="#"` pra `href="links-ferramentas.html"` nos 5 templates que replicam a sidebar. ### Rodada 17 — Ambiente ganhou Python 3.13 + Postgres — migração da rodada 16 aplicada O usuário avisou que "todas as ferramentas necessárias já estão instaladas" neste ambiente. Confirmado: o `.venv` do projeto já tem Django 6.0.7, DRF 3.17.1, psycopg, python-dotenv e Pillow 12.3.0 (Python 3.13.14), e há um Postgres local acessível pelas credenciais do `.env` — inclusive já com migrações antigas aplicadas até `0002_notificacaodispensada` (rodada 15), de uma sessão anterior fora deste histórico. Com o ambiente disponível, gerada e aplicada a migração pendente da rodada 16 (`0003_linkferramenta_and_more`: model `LinkFerramenta` + campo `gerencia_links_ferramentas` em `PerfilAcesso`) e reaplicado `seed_portal` — `gerencia_links_ferramentas=True` confirmado no perfil "Integração e Inovação". Ainda não testado num navegador de verdade (login + upload de ícone + reordenar + notificações persistindo entre reloads) — próximo passo natural se o usuário quiser essa validação. ### Rodada 19 — Permissão de Links & Ferramentas: de checkbox dedicado para visualizar/editar na árvore A permissão `gerencia_links_ferramentas` (criada na rodada 16 como `BooleanField` dedicado + checkbox solto no topo do card de edição de perfil) foi **revertida** a pedido do usuário: "ao invés de ter uma caixa de seleção acima, deixar uma opção onde marca a liberação para visualizar o Links & Ferramentas, com subseleções entre visualizar e editar. Pois teremos outras aplicações com a mesma funcionalidade." Ou seja: o pedido não era só um ajuste de UI, era um pedido de **modelo de dados reutilizável** pra qualquer módulo futuro que precise da mesma distinção visualizar/editar. Novo desenho (documentado em detalhe em `CLAUDE.md` da raiz → "Padrão visualizar/editar"): em vez de um campo dedicado por módulo, "visualizar" e "editar" passaram a ser **dois `apps` normais** de `links-ferramentas` em `catalogo.MODULE_APPS` — reaproveitando 100% a árvore de permissões genérica que já existia (`profiles.js`), sem nenhum código de UI novo. Isso também tornou o backend mais estrito: antes, `GET /api/links-ferramentas/` era liberado a qualquer autenticado; agora exige `apps.visualizar`, e a escrita exige `apps.editar` — ambos checados por uma única classe genérica `PermissaoApp(module_key, app_key)` (`permissions.py`) reaproveitável por qualquer módulo futuro com a mesma necessidade, sem precisar de subclasse nova. Removido nesta rodada: campo `gerencia_links_ferramentas` em `PerfilAcesso` (migração `0004_remove_perfilacesso_gerencia_links_ferramentas`), método `Usuario.gerencia_links_ferramentas()`, classe `PodeGerenciarLinksFerramentas`, o campo em `PerfilAcessoSerializer`/`PerfilResumoSerializer`/`me_view`, e o checkbox `#pa-gerencia-links-ferramentas` em `perfis-acesso.html`/`profiles.js`. Pegadinha resolvida no `seed_portal.py`: como `links-ferramentas` está em `BASE_KEYS` (habilitado pra todo perfil), `catalogo.permissions_from_keys()` habilitaria **todos** os apps do módulo de uma vez — incluindo "editar" pra todo mundo. Corrigido forçando `apps.editar = False` explicitamente pra qualquer perfil que não seja "Integração e Inovação" (código 8), depois de montar o dict de permissões. Confirmado via shell: os 7 perfis "normais" saíram com `visualizar=True, editar=False`; só o código 8 saiu com ambos `True`. Migração `0004` gerada e aplicada no ambiente local (Python 3.13/Postgres disponíveis desde a rodada 17); `manage.py check` limpo. ### Rodada 34 — Acessos Gerais — segunda aplicação de Links & Ferramentas Pedido, com uma tela do Asana como referência: um cadastro de acessos/logins compartilhados da equipe (ex.: login geral de um site), organizado em seções e linhas, com popup de detalhes por linha. Virou a segunda aplicação real da seção "Links & Ferramentas" — o item do menu, que antes era um link direto, passou a ser um `nav-group` expansível com dois sub-itens ("Links & Ferramentas" e "Acessos Gerais"), cada um favoritável separadamente. O que foi construído: dois models novos (`AcessoGeralSecao`, `AcessoGeral`, sem relação com `LinkFerramenta`), mesmo padrão visualizar/editar já usado em Links & Ferramentas (chaves próprias `acessos-gerais-visualizar`/`acessos-gerais-editar`); ordenação escopada por seção (não global); restrição opcional de seção por perfil (`perfis_restritos` M2M pra `PerfilAcesso` — filtro de **dado**, independente da árvore de permissões); e um editor de "Observações" com texto rico + imagens embutidas (`contenteditable`, colar/arrastar imagem vira `data:` URI, sem upload separado). Sanitização no backend via `nh3` (não `bleach`, sem manutenção desde 2023) — allowlist estrita de tags/atributos, permitindo só `` com esquema `data:` além de tags de texto básicas, contra XSS via HTML malicioso injetado no payload. Migrações `0016_acessogeralsecao_acessogeral` e `0017_acessogeralsecao_perfis_restritos_and_more`. Detalhe técnico completo em `docs/links-ferramentas-acessos-gerais/links-ferramentas-acessos-gerais.md`, seção "Acessos Gerais". ### Rodada 35 — Revisão de interface de 30/09/2026: Links & Ferramentas e Acessos Gerais Correções vindas da revisão de interface de 30/09/2026 (ver `plano.md`), só no frontend: - **Crítico, com backend**: criar ou renomear seção chamava `pidFetchPerfis()` (`/api/perfis/`, que exige `gerencia_permissoes`), então quem só tinha `acessos-gerais-editar` clicava e nada acontecia. Nova action `GET /api/acessos-gerais-secoes/perfis/` em `AcessoGeralSecaoViewSet` (só `codigo`/`nome`, exige `acessos-gerais-editar`) e `ensurePerfisCache()` passou a usá-la, com aviso se falhar. Mexe em `views.py`: exige reiniciar o `runserver`. - **Falha de carga**: nas duas telas o erro mostra "Não foi possível carregar. Recarregue a página; se persistir, contate a Inovação." no lugar da mensagem de lista vazia. - **Envio duplo**: salvar link (upload do ícone pode demorar), seção e acesso ficam travados ("Salvando…"/"Adicionando…") até a resposta; a estrela de favorito também trava durante a requisição. - **Falha silenciosa**: remover link, excluir seção/acesso, mover e arrastar passaram a mostrar `pidAlert` com título. Mover/arrastar sempre recarregam a lista do servidor depois, então uma ordem que a tela mostrou antes da hora é desfeita se a gravação falhar. - **Acessos Gerais, arrastar**: a linha muda de posição na hora (o array local é reordenado, antes só mudava depois do refresh). - **Sair sem salvar**: Cancelar e clique fora dos modais de link, seção e acesso comparam com o retrato da abertura (no acesso, inclusive o HTML das observações com imagem) e pedem confirmação quando houve alteração. - Ícone do cartão com `width`/`height="115"` e `loading="lazy"`; nome do cartão, título do popup e usuário/senha longos com `overflow-wrap: anywhere`; `overscroll-behavior: contain` nas observações; "2 MB" com espaço não separável; `spellcheck="false"` em URL e usuário.