# Plano — Portal De Paula (Protótipo Interno) ## Visão geral Reformulação do portal interno da De Paula Contadores. O pedido original foi "estruturar um protótipo visual, mas funcional, para avaliarmos e estruturarmos melhor o frontend do portal" — reaproveitando a essência do portal atual (referência: capturas de tela do sistema em produção, tema escuro roxo/dourado, sidebar com dezenas de seções, busca de aplicações, cards de atalho, login com logo circular), não recriando do zero. Decisões de arquitetura tomadas no início e válidas até a rodada 13: - **Stack: HTML + CSS + JS puro**, sem framework, sem build step, sem dependência externa além das fontes do sistema. - **Sem backend**: todo o estado (sessão, usuários, perfis de acesso, favoritos, widgets, compromissos, tema) vivia no `localStorage` do navegador. - **Visual**: modernizar a estrutura existente (sidebar + topbar + busca + cards + login), não clonar pixel a pixel as capturas de referência. Isso valeu como protótipo de validação de fluxo/desenho. Na rodada 13 o usuário definiu a stack real de backend e pediu a migração completa — ver essa seção para a arquitetura atual. Este documento foca no histórico de decisões, no que foi construído em cada rodada e no que ainda está em aberto; para arquitetura técnica atual (páginas, API, ordem de scripts, modelo de permissões, como o CSS está dividido entre arquivos), ver `CLAUDE.md`. ## Cronologia de construção ### 1. Protótipo inicial — login + shell Tela de login (logo, usuário/senha, "esqueceu senha" apontando para Integração e Inovação) e o shell principal: sidebar com as 14 seções do documento original de arquitetura, topbar com hamburger/"Acessar Ramais"/notificações, busca de aplicações e uma grade de cards de atalho fixos (Links, OS, Ramais, Relatórios, Calendário, Solicitações). Tema claro/escuro com toggle salvo em `localStorage`. Um modal de "simular perfil de acesso" (persona) deixava escolher entre as 7 personas do documento original para ver o menu mudar — mecanismo **removido depois** (ver item 4). ### 2. Perfis de Acesso Tela de administração (`perfis-acesso.html`) com lista + edição de perfis: Código, Nome, árvore de permissões por módulo, aba "Usuários do Escritório". Criado um perfil especial **"Integração e Inovação"**, com acesso total e uma capacidade exclusiva — `gerenciaPermissoes` — que também gate-ia o acesso à própria tela de Perfis de Acesso. ### 3. Usuários Tela de cadastro de contas (`usuarios.html`), movida para dentro do grupo "Administração" no menu (junto de Perfis de Acesso), com a mesma restrição: só quem tem `gerenciaPermissoes` acessa e pode cadastrar/editar/excluir usuários. ### 4. Login real (fim do simulador de persona) O usuário pediu para poder testar de verdade o cadastro de acessos — "podemos testar na realidade como irá funcionar". Isso trocou o mecanismo inteiro: - Login parou de aceitar qualquer senha; passou a validar contra contas reais em `pid_users_v1`/`v2` (`assets/js/users.js`). - Duas contas de demonstração criadas: `gabriel`/`gabriel` (perfil Integração e Inovação) e `bruno`/`bruno` (sem perfil vinculado, para testar o estado "sem acesso"). - O modal de simulação de persona foi **removido**; a visibilidade do menu passou a vir do perfil de acesso de verdade vinculado ao usuário logado (`access.js` → `pidResolveAccess`). - Cada um dos 7 perfis do documento original ganhou um registro real em Perfis de Acesso (Diretoria, Departamento Pessoal, Gerencial, Legalização, Fisco/Contábil, Financeiro, Protocolo), mais Integração e Inovação (8 ao todo). ### 5. Múltiplos perfis por usuário Pedido explícito: "algumas pessoas podem ter acesso ao perfil financeiro e fisco/contábil". `profileCodigo` (número único) virou `profileCodigos` (array) em `pid_users`; a resolução de acesso em `access.js` passou a fazer **união** entre todos os perfis vinculados a um usuário — uma seção aparece se qualquer um dos perfis do usuário conceder acesso a ela. ### 6. Bugs encontrados e corrigidos Três bugs reais surgiram e foram corrigidos durante o uso: - **`users-admin.js` ficou preso na assinatura antiga** de `pidResolveAccess()` (`{ profile }` em vez de `{ profiles }`) depois da migração do item 5 — a tela de Usuários redirecionava sempre de volta pro Principal antes de renderizar, dando a impressão de "não abre". Corrigido ajustando a desestruturação. - **CSS sobrescrevendo o atributo `hidden`**: componentes que definem seu próprio `display` (`.no-access`, `.notif-badge`, `.app-card`) ignoravam o `hidden` do JS, porque uma regra de autor com `display` explícito vence a regra padrão do navegador `[hidden]{display:none}` mesmo com especificidade igual. A mensagem de "sem perfil vinculado" continuava aparecendo por cima do dashboard normal mesmo com o perfil já vinculado. Corrigido com uma regra global em `base.css`: `[hidden] { display: none !important; }` — resolveu esse caso e mais dois latentes (badge de notificação, filtro de busca nos cards). - **Submenus recolhidos continuavam clicáveis/focáveis**: o accordion do sidebar só usava `max-height:0` + `overflow:hidden` para esconder, o que não impede clique nem navegação por Tab. Corrigido adicionando `visibility:hidden` + `pointer-events:none` ao estado fechado. ### 7. Permissão granular por aplicação A árvore de permissões de cada módulo mostrava ações genéricas (Visualizar/Incluir/Editar/Excluir), sem relação com o conteúdo real do menu. Trocado por uma lista das aplicações reais de cada seção (ex.: em Portais, os checkboxes viraram "Portal do Cliente" e "Portal Fiscal") — e isso passou a **funcionar de verdade**: desmarcar uma aplicação esconde só aquele item do sidebar (`data-app` + `access.js`), não o módulo inteiro. Nesta mesma rodada, o campo **Ambiente** (Escritório/Empresa) foi removido do cadastro de perfil — "trabalharemos como portal interno, sem mais de um ambiente". ### 8. Favoritos Pedido: "toda aplicação deve poder ser favoritada, para adicionar na tela inicial". Implementado sem precisar marcar cada item do menu manualmente — `favorites.js` deriva um ID estável a partir do próprio texto/estrutura do sidebar (ver `CLAUDE.md` → "Favoritos") e injeta uma estrela ao lado de cada aplicação. A tela Principal deixou de ter cards fixos (Links, OS, Ramais...) e passou a ser inteiramente dirigida pelos favoritos de cada usuário — o antigo `cards.js` foi removido. ### 9. Calendário De Paula → link externo "O Calendário De Paula deve ser um redirecionador" para `https://depaula-tvcorporativa.lovable.app/calendario` (abre em nova aba). O calendário interno mockado (`calendario.html` + `calendar.js`, com eventos fixos por setor) foi removido a pedido do usuário depois da troca — não recriar sem confirmar. ### 10. Calendário Individual + Widgets Pedido novo, em duas partes: - **Calendário Individual** (`calendario-individual.html`): agenda pessoal de cada colaborador (seção base, disponível a todo perfil). Cada um cria compromissos e pode marcá-los como visíveis para todos que compartilham pelo menos um perfil de acesso em comum (`sharedWithProfile` + `pidEventsVisibleTo` em `events.js`) — colegas só visualizam, não editam nem excluem compromisso alheio. - **Widgets** na tela Principal: área "Widgets" com botão "Adicionar Widget" (seletor, no estilo Asana) — pensada para crescer além de um único tipo. Hoje só existe o widget de Calendário Individual (próximos 5 compromissos), mas o registro `PID_WIDGET_TYPES` já está estruturado para novos tipos sem refazer a área de adicionar/remover. ### 11. Horário e edição no Calendário Individual Compromissos ganharam campo de horário (ordenados por horário dentro do dia) e passaram a ser editáveis depois de criados — clicar num compromisso próprio abre o mesmo modal preenchido, com um botão "Excluir" adicional, em vez de excluir direto ao clicar. ### 12. Menu de conta O chip do canto superior direito mostrava o **perfil/departamento** ("Departamento Pessoal" etc.) — trocado para mostrar o **usuário logado** (nome + ícone de pessoa). Clicar abre um menu com nome + perfis vinculados, "Alterar senha" (modal com senha atual/nova/confirmação, validada contra `pid_users`) e, só para quem tem `gerenciaPermissoes`, um atalho para Perfis de Acesso (antes era o clique direto no chip que levava pra lá). Nesta rodada os estilos de campo de modal (`.modal-field`, `.modal-actions` etc.), que só existiam em `calendario.css`, foram generalizados para `components.css`, já que agora mais de uma tela precisa deles. ### 13. Migração para backend Django + PostgreSQL O usuário definiu a stack real do backend: **Python 3.13 + Django 6.0 + PostgreSQL 14**, e pediu três coisas na mesma rodada: 1. Construir esse backend completo (models, API REST, banco Postgres) — não só adaptar o frontend a um contrato assumido. 2. Migrar para o banco **tudo** que hoje existia no `localStorage`, exceto a preferência de tema: usuários, perfis de acesso, favoritos, widgets e compromissos do Calendário Individual. 3. Autenticação via **sessão/cookie do Django** (decisão explícita, não token/JWT) — o que levou à escolha de servir o frontend estático pelo próprio Django (mesma origem), evitando complicação de cookie de sessão cross-origin. O que mudou estruturalmente: - Projeto Django novo, inicialmente em `Portal/backend/` (`config/` + app único `portal_api/`) — reorganizado na rodada 14 para o padrão de pastas de um projeto Django de verdade. Ver `CLAUDE.md` → "Arquitetura" para o detalhamento de cada arquivo e a tabela de endpoints. - Model de usuário customizado (`Usuario`, estendendo `AbstractUser`) desde o início — trocar depois de criado o projeto é doloroso. - Senha passou a usar hashing nativo do Django (`set_password`/ `check_password`) — antes era texto puro no protótipo; melhoria de segurança real, não só troca de armazenamento. - Catálogo de módulos/aplicações do menu (antes `PID_MODULES`/ `PID_MODULE_APPS` hardcoded em `profiles.js`) virou `portal_api/catalogo.py`, fonte única da verdade, exposto só leitura via `GET /api/catalogo/`. - A união de permissões entre múltiplos perfis de um usuário (rodada 5), antes calculada no cliente (`access.js` → `pidResolveAccess`/ `pidApplyAccessVisibility`), passou a ser calculada uma única vez no servidor (`permissoes_efetivas()` em `views.py`) e consumida pronta via `GET /api/me/`. - A lógica de "quem vê qual compromisso" do Calendário Individual (rodada 10 — `pidEventsVisibleTo`) também migrou pro backend (`CompromissoAgendaViewSet.get_queryset`). - Todo o frontend que lia/escrevia `localStorage` foi reescrito para `async`/`fetch` contra a API (`assets/js/api.js`, hoje `static/js/api.js` — ver rodada 14 — é o módulo novo), mantendo a mesma estrutura de arquivos e nomes de função sempre que possível para minimizar o tamanho do diff. - `assets/js/users.js` (armazenamento local de usuários) foi **removido** — usuários agora só existem no banco, via `/api/usuarios/`. **Limitação do ambiente onde essa rodada foi feita**: só havia Python 3.8 e nenhum servidor PostgreSQL disponíveis, então não foi possível rodar `makemigrations`/`migrate`/`runserver` nem testar o fluxo ponta a ponta durante a implementação — o código foi revisado estaticamente com cuidado (sintaxe, convenções do DRF, coerência entre serializers/views/frontend). Rodar de verdade — incluindo gerar a migração inicial — fica para o ambiente com Python 3.13 + Postgres 14. ### 14. Reorganização de pastas no padrão Django O backend tinha sido criado numa subpasta `Portal/backend/`, com o frontend (HTML/CSS/JS) solto na raiz de `Portal/` ao lado dela — funcional, mas não era a estrutura convencional de um projeto Django. Pedido: "reorganize a estrutura das pastas, conforme o padrão de um projeto Django". Mudanças: - `manage.py`, `requirements.txt`, `.env`, `config/` e `portal_api/` subiram de `Portal/backend/` para a raiz de `Portal/` — `Portal/` passou a **ser** o projeto Django, não conter um subprojeto. - As 5 páginas HTML foram movidas para `Portal/templates/` (antes soltas na raiz) — é para lá que `TEMPLATES[0]["DIRS"]` aponta agora. - `assets/css`, `assets/js` e `img/` viraram `static/css`, `static/js` e `static/img` (pasta `assets/` removida) — `STATICFILES_DIRS` (novo em `settings.py`) aponta pra lá, e `STATIC_ROOT` foi adicionado para `collectstatic` em produção. - Os 5 templates passaram a usar `{% load static %}` + `{% static 'css/x.css' %}` em vez de caminhos hardcoded (`assets/css/x.css`) — forma idiomática do Django de referenciar estáticos. - `config/urls.py` perdeu os `re_path`/`static_serve` manuais para `assets/`/`img/` — `django.contrib.staticfiles` já serve `static/` automaticamente em `DEBUG` a partir de `STATICFILES_DIRS`, sem configuração extra. - Bug corrigido de passagem: o `.env` já existia (com as credenciais do Postgres) mas `settings.py` nunca chamava `load_dotenv()` — ele nunca tinha sido lido de verdade. Corrigido ao mexer em `settings.py` por causa da mudança de `BASE_DIR`. Nenhuma mudança de comportamento — só de organização de arquivos; os endpoints da API, os models e a lógica do frontend continuam os mesmos da rodada 13. ### 15. Dispensar notificações persiste por usuário Bug relatado: ao clicar no X de uma notificação individual, o card do dropdown fechava inteiro (comportamento incorreto) e, além disso, qualquer notificação dispensada (X individual ou "Limpar tudo") voltava a aparecer depois de um reload da página. - **Fechamento indevido do dropdown**: causado pela ordem de eventos — `list.innerHTML` era reconstruído (removendo o botão clicado do DOM) antes do clique terminar de subir até o listener global em `document` que fecha o dropdown em clique fora; como o botão já não estava mais na árvore, `dropdown.contains(event.target)` avaliava falso, fechando o card como se fosse clique externo. Corrigido com `event.stopPropagation()` no handler de dismiss em `notifications.js`. - **Notificação reaparecendo no reload**: era o comportamento documentado até então (`notifications.js` guardava a lista só em memória, "zera a cada reload") — o usuário pediu explicitamente para persistir no backend em vez de usar `localStorage`. Criado o model `NotificacaoDispensada` (chave natural `usuario` + `notif_id`, mesmo padrão de `Favorito`/`WidgetUsuario`) e o endpoint `/api/notificacoes-dispensadas/`. `notifications.js` agora busca os IDs já dispensados no carregamento e filtra a lista antes de renderizar, e persiste via POST a cada X clicado ou "Limpar tudo". Importante: só o **estado de dispensada** passou a ser real — o conteúdo das notificações de "nova ferramenta" (`PID_NEW_TOOLS_NOTIFICATIONS`) continua mockado/hardcoded no frontend, só os itens de compromisso vêm de dados reais. ### 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). - **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. - **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. Mesma limitação de ambiente das rodadas anteriores: sem Python 3.13/Postgres neste ambiente, não foi possível rodar `makemigrations`/`migrate` (novo model + dois campos novos em `PerfilAcesso`) nem testar upload de imagem de ponta a ponta. ### 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. A limitação documentada nas rodadas 13–16 (só Python 3.8, sem Django, sem Postgres) não vale mais — `CLAUDE.md` foi corrigido pra refletir isso. 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. ### 18. Bug visual: espaçamento inconsistente na sidebar Reportado com print: itens do menu com estrela de favorito (qualquer ``/`.nav-subitem`, exceto "Principal") pareciam ter espaçamento diferente dos itens sem estrela (toggles de grupo como Auditorias/Solicitações, que são `