feat(products): submenu de Subgrupos aninhado dentro de Grupos

Mesma lógica do submenu "Produtos" já existente: "Grupos" agora expande
seu próprio submenu com "Subgrupos" (AppShell ganha suporte a um
segundo nível de accordion aninhado). Nova tela lista todos os
subgrupos de todos os grupos, com coluna Grupo e criação via dropdown
de grupo explícito — complementa (não substitui) o painel já existente
dentro da tela de um grupo específico.

listProductSubgroups passa a resolver group_name via JOIN.

Suíte: 95/95. pnpm turbo run typecheck build: 28/28 (core-web,
reseller-web e subscriber-web confirmados sem regressão no AppShell
compartilhado).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-18 08:52:19 -03:00
parent f38f2407ff
commit 96200dc2ba
9 changed files with 200 additions and 20 deletions

View File

@@ -646,3 +646,18 @@ Menu "Produtos" ganhou submenu: Itens/Tipos/Grupos/Marcas/Unidades (Subgrupo del
### Pendências
Suítes de teste futuras vão continuar acrescentando linhas `qa-test-*` permanentes nesses 5 catálogos (sem rota de exclusão física, mesmo padrão aceito para os catálogos fiscais) — precisa de desativação manual periódica até (se algum dia for justificado) existir uma rota de exclusão física ou um processo de limpeza dedicado. "Não dá para limpar Grupo/Marca de volta para vazio via PATCH" continua uma limitação pré-existente e mais ampla do padrão `COALESCE` usado em `updateProduct` — não corrigida (sem risco de invariante quebrada, ao contrário do caso do subgrupo).
---
## Submenu de Subgrupos dentro de Grupos — segundo nível de navegação
Data: 2026-09-18. Usuário pediu, sobre a leva anterior: "dentro da opção grupos tem que abrir um sub menu de sub grupos para que fique na mesma lógica" — ou seja, um segundo nível de submenu (Produtos > Grupos > Subgrupos), não só o painel "Ver subgrupos" já existente dentro da tela de Grupos. Implementado diretamente (mudança pequena e bem entendida, sem delegar).
`packages/ui/src/components/AppShell.tsx`: `NavGroup` passou a ser recursivo — um filho que também tem `children` agora renderiza como outro `NavGroup` aninhado (antes só 1 nível era suportado, documentado explicitamente como limitação). `withActive()` em `AuthenticatedLayout.tsx` já era recursivo (nenhuma mudança necessária ali) — só o item "Grupos" ganhou seu próprio `children`: `[Grupos (lista), Subgrupos]`.
Nova tela `apps/core-web/src/routes/products/catalogs/SubgroupsListPage.tsx` (`/products/subgroups`) — todos os subgrupos, de todos os grupos, com coluna "Grupo" e formulário de criação que pede o Grupo explicitamente (dropdown), diferente do `GroupSubgroupsPanel` já existente (que fica dentro da tela de um Grupo específico e por isso nunca precisa perguntar "qual grupo" — o contexto já implica). As duas telas coexistem deliberadamente: uma é a visão geral alcançável pelo menu, a outra é o atalho contextual ao editar um grupo específico.
Pequeno complemento de backend necessário para a coluna "Grupo" da tela nova: `listProductSubgroups` (`packages/database/src/repositories/product-catalogs.ts`) passou a fazer JOIN com `product_groups` e devolver `group_name` — antes só existia `group_id` cru, suficiente para o painel escopado (que já sabe o nome do grupo pelo contexto) mas não para a listagem geral.
### Verificação
`pnpm turbo run typecheck build` 28/28 (os 3 apps que usam `@eden/ui` — `core-web`/`reseller-web`/`subscriber-web` — confirmados sem regressão). Teste novo em `apps/api/test/product-catalogs.spec.ts` cobrindo `group_name` resolvido tanto na listagem filtrada por `groupId` quanto na geral. Suíte completa: 95/95 verdes. Testado ao vivo via `curl` contra `eden-api`/`eden-core` reconstruídos: rota `/products/subgroups` responde 200, criação de subgrupo segue funcionando, listagem geral resolve `group_name` corretamente. Interação de clique no accordion aninhado (abrir "Grupos" → ver "Subgrupos" → navegar) não foi possível verificar via browser automatizado (nenhuma ferramenta desse tipo disponível neste ambiente) — verificação limitada a build/tipos/API real, mesma limitação já registrada pelo agente de frontend na leva anterior.