portal_publico/docs/favoritos.md

2.3 KiB

Favoritos: como o ID de uma aplicação é derivado

Movido do CLAUDE.md da raiz em 2026-08-26 para reduzir conflito de edição entre aplicações. Ver CLAUDE.md na raiz para arquitetura geral/transversal do Portal. Este arquivo não é auto-carregado pelo Claude Code — leia manualmente ao mexer em favorites.js ou no sidebar/menu.

favorites.js não depende de nenhum atributo data-* dedicado para identificar "o que é favoritável" — ele varre .sidebar a.nav-item, .sidebar a.nav-subitem e deriva um ID estável a partir da própria estrutura/texto do menu (pidCollectFavoritableApps), igual a antes da migração. Esse ID é o que vira app_id em POST /api/favoritos/ e na URL de DELETE /api/favoritos/{app_id}/:

  • Se o <li> do link já tem data-section, o ID é esse valor (ex.: "ramais").
  • Caso contrário (é um sub-item dentro de um nav-group), o ID é "<data-section do grupo pai>__<slug do texto do link>" (ex.: "portais__portal-do-cliente").

O slug (pidSlug) normaliza acentos (NFD) e troca sequências de caracteres não [a-z0-9] por -. Se o texto de um label mudar, o app_id derivado muda junto (favoritos existentes referenciando o ID antigo deixam de casar) — mesmo caveat vale se a estrutura do menu mudar (ex.: um item vira nav-group expansível, ver docs/links-ferramentas-acessos-gerais.md, "Links & Ferramentas": um favorito antigo app_id === "links-ferramentas" parou de casar quando a seção virou dois sub-itens).

Reordenar os cards favoritos: Favorito.ordem (PositiveIntegerField, Meta.ordering = ["ordem", "id"]) — mesmo padrão de LinkFerramenta/WidgetUsuario: FavoritoViewSet.perform_create atribui ordem = max(ordem atual do usuário) + 1, e reordenar é drag-and-drop nativo em #app-card-grid (favorites.js, dragstart/dragover/drop, PATCH /api/favoritos/{app_id}/ só nos itens cujo ordem mudou) — mesma mecânica de Widgets/Links & Ferramentas (ver docs/calendario-individual.md/docs/links-ferramentas-acessos-gerais.md). .app-card inteiro é draggable="true" (não precisa de um handle separado como os widgets, já que não tem resize pra conflitar); o botão de remover (.app-card__remove) é draggable="false" pra não interferir.

.app-card* mora em components.css (junto de outra UI genérica reutilizável).