Conn2Flow
Navegação das docs

SDD

Human Request: Hierarquia Multi-nível de Menus e Drag-and-Drop Estilo WordPress (req-016)

Esta especificação define a evolução do módulo de menus para suportar itens multi-nível (pais e filhos), múltiplos tipos de itens (páginas, links livres, cabeçalhos, ações…

Esta especificação define a evolução do módulo de menus para suportar itens multi-nível (pais e filhos), múltiplos tipos de itens (páginas, links livres, cabeçalhos, ações programáticas), um editor drag-and-drop vertical/horizontal para organizar a estrutura hierárquica e filtros por tipo de página no autocomplete.


1. Escopo das Modificações

1.1 Tipos de Itens do Menu

Cada item adicionado ao menu deve possuir uma tipagem bem definida que controla seu comportamento no frontend e backend:

  1. Página (pagina): Vincula a uma página física existente do site (carregada via AJAX de paginas). O link é resolvido dinamicamente no renderizador.

  2. Link Externo/Livre (link-custom): O usuário digita de forma livre o texto (Rótulo/Label) e a URL completa (ex: [REDACTED] ou /contato/).

  3. Cabeçalho/Contêiner (cabecalho): Apenas rótulo textual, sem link (URL vazia ou #). Usado para agrupar e abrir submenus suspensos.

  4. Link de Ação / CSS Personalizado (link-action): Permite que o usuário insira classes CSS personalizadas no item de menu para que o desenvolvedor possa disparar modais ou scripts programáticos no frontend.

  5. Divisor / Separador (separador) (Sugestão do Arquiteto): Apenas uma linha horizontal divisória sem texto ou link.

1.2 Filtro de Tipo de Página no Autocomplete

  • Contexto: No Conn2Flow, as páginas do site registradas na tabela paginas possuem tipos diferenciados na coluna tipo (principalmente 'pagina' e 'sistema').

  • Filtro Visual: Quando o usuário selecionar o tipo de menu pagina, deve ser exibido um conjunto de campos de rádio (inline radio checkboxes) do Fomantic UI contendo as opções:

    • Página (value="pagina"): Pesquisa apenas páginas de conteúdo padrão.

    • Sistema (value="sistema"): Pesquisa apenas páginas de rotinas/funcionalidades do sistema.

    • Ambos (value="ambos"): Pesquisa qualquer tipo de página.

  • Comportamento: A opção padrão selecionada deve ser Página. A alteração dessa seleção deve recarregar os dados do autocomplete enviando o parâmetro correto ao AJAX.

1.3 Estrutura de Células de Template no Widget (Recursão)

No motor de widgets (menus.widget.php), os itens de menu serão processados como uma árvore hierárquica (JSON recursivo) em vez de uma lista plana. Para renderizar menus multiníveis, os templates em resources/{pt-br,en}/templates/ suportarão três delimitadores:

  1. no-item (<!-- no-item < --> ... <!-- no-item > -->): Renderizado quando o menu não possuir nenhum item.

  2. item (<!-- item < --> ... <!-- item > -->): Célula padrão para itens finais (folhas, sem sub-itens).

  3. item-parent (<!-- item-parent < --> ... <!-- item-parent > -->): Célula para itens que possuem filhos. Deve renderizar os dados do item pai e incluir o placeholder especial [[item#children]] para injetar a renderização recursiva da lista de sub-itens filhos.


2. Detalhes de Implementação no Administrador

2.1 Interface de Adição de Itens (CRUD e Autocomplete)

  • Formulário de Entrada:

    • Nas páginas adicionar.html, editar.html e clonar.html do módulo menus, expandir a seção de curadoria.

    • Adicionar o seletor de "Tipo de Item" (Dropdown do Fomantic UI): Páginas, Link Customizado, Cabeçalho, Link com Classe CSS ou Separador.

    • Adicionar a estrutura de rádio inline para filtrar o tipo de página (inicialmente oculta, aparecendo apenas quando o tipo de item for pagina):

      <div class="field" id="page-type-filter-wrapper" style="display:none; margin-bottom: 12px;">
      <div class="inline fields">
          <label>Tipo de Página</label>
          <div class="field">
              <div class="ui radio checkbox" data-tooltip="Clique para mostrar tipo Página" data-position="top left" data-inverted="">
                  <input type="radio" name="page_search_type" value="pagina" checked="checked">
                  <label>Página</label>
              </div>
          </div>
          <div class="field">
              <div class="ui radio checkbox" data-tooltip="Clique para mostrar tipo Sistema" data-position="top left" data-inverted="">
                  <input type="radio" name="page_search_type" value="sistema">
                  <label>Sistema</label>
              </div>
          </div>
          <div class="field">
              <div class="ui radio checkbox" data-tooltip="Clique para mostrar ambos" data-position="top left" data-inverted="">
                  <input type="radio" name="page_search_type" value="ambos">
                  <label>Ambos</label>
              </div>
          </div>
      </div>
      </div>
    • Dependendo do tipo escolhido, exibir os campos de entrada corretos:

    • Para pagina: Exibir o filtro de tipo de página, e o campo de busca autocomplete AJAX (#manual_search_input).

    • Para link-custom: Inputs textuais "Rótulo do Menu" e "URL".

    • Para cabecalho: Input textual "Rótulo do Menu" apenas.

    • Para link-action: Inputs "Rótulo", "URL" e "Classes CSS Personalizadas".

    • Para separador: Sem inputs adicionais (botão "Adicionar Separador").

  • Local de Inserção:

    • Se houver um item atualmente clicado/selecionado na lista de menus, o novo item deve ser adicionado imediatamente abaixo dele (como irmão ou como filho). Se não houver nenhum selecionado, adicionar no final da raiz.

2.2 Drag-and-Drop Multinível (menus.js)

  • Comportamento Hierárquico (Estilo WordPress):

    • As tags selecionadas passam a se organizar em uma lista vertical estruturada (ex: elementos <li> ordenáveis dentro de um <ol>).

    • Arrastar um item horizontalmente/verticalmente serve tanto para definir a ordenação (quem vem antes ou depois) quanto para recuar/avançar o recuo e definir o aninhamento como filho (children) do item imediatamente acima.

    • Uso do Sortable.js: Configurar o Sortable.js com instâncias aninhadas (nested groups com group: 'nested') permitindo que sub-listas aceitem elementos arrastados, ou integrar um plugin leve de jQuery compatível (como nestedSortable) que facilite o arraste bidimensional (vertical e recuo horizontal) para construir a árvore.

    • Edição In-line: Cada item da árvore deve exibir:

    • Um botão/ícone de edição para abrir um pequeno modal/painel in-line que permita alterar o Rótulo, URL ou Classes CSS do item específico. No caso de item tipo página, ao ser selecionada pelo autocomplete, ela preenche o rótulo/título automaticamente, mas permite que o usuário altere ou resuma o rótulo livremente.

    • O botão de exclusão ("x") que remove o item e todos os seus filhos recursivamente da árvore.

2.3 Persistência do Schema

  • O array schema.selected_items passa a salvar a árvore hierárquica completa em JSON estruturado de objetos:

    [
    {
      "id": "item_17497491",
      "type": "cabecalho",
      "label": "Notícias",
      "url": "#",
      "css_classes": "",
      "children": [
        {
          "id": "item_17497495",
          "type": "pagina",
          "label": "Economia",
          "url": "/economia/",
          "page_id": "pagina-economia",
          "children": []
        }
      ]
    }
    ]
  • No submit do formulário e no AJAX do preview, serializar a árvore em fields_schema de forma que o widget receba a estrutura hierárquica correta.


3. Detalhes de Implementação no Backend (menus.php / menus.widget.php)

3.1 Filtro de Páginas no AJAX de Busca

  • Local: gestor/modulos/menus/menus.php

  • Lógica:

    • Na função menus_ajax_pages_search(), extrair o parâmetro tipo enviado pelo frontend (ex: $_REQUEST['params']['tipo'] ou $_REQUEST['tipo']).

    • Se tipo for 'pagina' ou 'sistema', concatenar no filtro SQL: AND p.tipo = '<VALOR>'.

    • Se tipo for 'ambos', não filtrar por tipo (trazer qualquer página ativa).

    • Caso o parâmetro seja omitido, aplicar o filtro por padrão como tipo = 'pagina'.

3.2 Lógica Recursiva no Renderizador

  • Local: gestor/modulos/menus/menus.widget.php

  • Lógica:

    • Na função de renderização, decodificar fields_schema obtendo a árvore de itens de menu.

    • Implementar uma função recursiva menus_render_level($items, $templates) que:

      1. Percorra os itens do nível atual.

      2. Para cada item sem filhos, carregue e substitua as variáveis no delimitador item (expondo [[item#label]], [[item#url]], [[item#slug]] e [[item#css_classes]]).

      3. Para cada item com filhos, carregue o delimitador item-parent, resolva suas variáveis, renderize recursivamente seus filhos via menus_render_level($item['children'], ...) e substitua a saída renderizada no placeholder [[item#children]].

      4. Concatene o HTML resultante e o devolva.

  • Resolução de Links:

    • Para o tipo pagina, buscar dinamicamente o link da página do banco usando o page_id (slug) e a linguagem ativa para garantir links canônicos sempre atualizados.


4. Regras e Versionamento

  • TASK DE RECURSOS: Ao finalizar a implementação e migrações, executar php ./gestor/controladores/agents/arquitetura/atualizacao-dados-recursos.php para validar as modificações.

  • ATENÇÃO: NÃO realizar commits (git commit) ou envios (git push) de forma autônoma. As alterações do executor devem permanecer locais para aprovação manual.

O Futuro é Híbrido: Humano + IA.

O Conn2Flow é a ferramenta. Se você precisa do Arquiteto para desenhar a estratégia da sua empresa usando essa tecnologia...

Contrate Consultoria Especializada