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:
@@ -126,6 +126,7 @@ export const updateProductGroup = (id: string, input: UpdateProductGroupInput) =
|
||||
|
||||
export interface ProductSubgroup extends SimpleCatalogRow {
|
||||
group_id: string;
|
||||
group_name: string;
|
||||
}
|
||||
|
||||
export interface CreateProductSubgroupInput extends CreateSimpleCatalogEntryInput {
|
||||
@@ -142,14 +143,18 @@ export async function listProductSubgroups(
|
||||
const params: unknown[] = [];
|
||||
if (groupId) {
|
||||
params.push(groupId);
|
||||
conditions.push(`group_id = $${params.length}`);
|
||||
conditions.push(`ps.group_id = $${params.length}`);
|
||||
}
|
||||
if (!includeInactive) {
|
||||
conditions.push("active = true");
|
||||
conditions.push("ps.active = true");
|
||||
}
|
||||
const where = conditions.length ? `WHERE ${conditions.join(" AND ")}` : "";
|
||||
const { rows } = await query<ProductSubgroup>(
|
||||
`SELECT * FROM product_subgroups ${where} ORDER BY name ASC`,
|
||||
`SELECT ps.*, pg.name AS group_name
|
||||
FROM product_subgroups ps
|
||||
JOIN product_groups pg ON pg.id = ps.group_id
|
||||
${where}
|
||||
ORDER BY ps.name ASC`,
|
||||
params,
|
||||
);
|
||||
return rows;
|
||||
|
||||
@@ -12,7 +12,10 @@ export interface AppShellNavItem {
|
||||
* When present (even if empty), this item renders as an expandable
|
||||
* accordion group instead of a direct link — `href` is then only a
|
||||
* placeholder (typically duplicated by the first child) and is never
|
||||
* navigated to directly. Only one level deep is supported.
|
||||
* navigated to directly. A child may itself declare `children`, nesting
|
||||
* another accordion one level deeper (e.g. Produtos > Grupos >
|
||||
* Subgrupos) — depth beyond that is untested but not structurally
|
||||
* prevented.
|
||||
*/
|
||||
children?: AppShellNavItem[];
|
||||
}
|
||||
@@ -101,16 +104,20 @@ function NavGroup({
|
||||
</button>
|
||||
{open ? (
|
||||
<div className="ms-3 mt-1 flex flex-col gap-1 border-s border-border-color ps-3">
|
||||
{(item.children ?? []).map((child) => (
|
||||
<LinkComponent
|
||||
key={child.key}
|
||||
href={child.href}
|
||||
className={cn(NAV_LINK_CLASSES, child.active ? NAV_LINK_ACTIVE_CLASSES : NAV_LINK_INACTIVE_CLASSES)}
|
||||
>
|
||||
{child.icon}
|
||||
<span>{child.label}</span>
|
||||
</LinkComponent>
|
||||
))}
|
||||
{(item.children ?? []).map((child) =>
|
||||
child.children ? (
|
||||
<NavGroup key={child.key} item={child} LinkComponent={LinkComponent} />
|
||||
) : (
|
||||
<LinkComponent
|
||||
key={child.key}
|
||||
href={child.href}
|
||||
className={cn(NAV_LINK_CLASSES, child.active ? NAV_LINK_ACTIVE_CLASSES : NAV_LINK_INACTIVE_CLASSES)}
|
||||
>
|
||||
{child.icon}
|
||||
<span>{child.label}</span>
|
||||
</LinkComponent>
|
||||
),
|
||||
)}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user