Conn2Flow
Navegação das docs

SDD

Human Request: Refatoração de Curadoria Manual, Autocomplete Ajax e Reordenação Drag and Drop (req-014)

Esta especificação detalha a substituição do dropdown customizado de curadoria manual por um componente de Autocomplete feito do zero (usando input básico do Fomantic UI, busca…

Esta especificação detalha a substituição do dropdown customizado de curadoria manual por um componente de Autocomplete feito do zero (usando input básico do Fomantic UI, busca dinâmica via AJAX, e labels reordenáveis com drag-and-drop), além de corrigir a inicialização e estilização do editor CodeMirror na aba "Código do Widget".


1. Escopo das Alterações e Justificativa

1.1 Autocomplete de Curadoria do Zero

  • Motivo: O componente jquery-custom-dropdown.js (baseado no dropdown múltiplo do Fomantic UI) apresentou sucessivos problemas de sincronização de estado, concorrência de callbacks de inserção/remoção, e falha na ordenação cronológica dos cliques.

  • Solução: Remover completamente o jquery-custom-dropdown.js. Em seu lugar, implementar no módulo de curadoria um campo de busca simples (<input type="text">), exibindo as sugestões retornadas dinamicamente via AJAX em uma lista flutuante. Ao selecionar uma sugestão, o item é adicionado como uma tag (Fomantic UI Label) abaixo do campo.

  • Ordenação Drag-and-Drop: As tags adicionadas devem suportar arraste e soltura (Drag-and-Drop) para reordenação manual (usando a biblioteca Sortable.js). Toda movimentação ou remoção de tag deve atualizar o array schema.selected_items e disparar imediatamente a atualização da pré-visualização ao vivo (scheduleWidgetPreview).

1.2 Restauração do CodeMirror na Aba do Widget

  • Motivo: O CodeMirror que exibe o código do widget na aba "Código do Widget" (#hep-widget-code) parou de renderizar, voltando a ser um <textarea> HTML padrão devido à remoção indevida da função updateWidgetCodeTab no lote anterior.

  • Solução: Restaurar a inicialização automática do CodeMirror com tema escuro (tomorrow-night-bright), altura (800px), modo de leitura-apenas (readOnly: true), e proteção contra duplicação de instâncias no DOM ao mudar de aba.


2. Detalhes de Implementação

2.1 Remoção do Componente Antigo

  • Deletar arquivo: gestor/assets/interface/jquery-custom-dropdown.js [DELETE]

  • Remover inclusões no PHP:

    • No arquivo gestor/modulos/publisher-highlights/publisher-highlights.php [MODIFY], remover as três chamadas de inclusão de biblioteca para jquery-custom-dropdown dentro das funções publisher_highlights_adicionar(), publisher_highlights_editar() e publisher_highlights_clonar():

      // REMOVER:
      gestor_pagina_javascript_incluir('biblioteca',[
      'caminho' => 'jquery-custom-dropdown',
      'biblioteca' => 'interface',
      ]);

2.2 Estrutura HTML nos Templates das Páginas

  • Locais:

    • gestor/modulos/publisher-highlights/resources/pt-br/pages/publisher-highlights-adicionar/publisher-highlights-adicionar.html [MODIFY]

    • gestor/modulos/publisher-highlights/resources/pt-br/pages/publisher-highlights-editar/publisher-highlights-editar.html [MODIFY]

    • gestor/modulos/publisher-highlights/resources/pt-br/pages/publisher-highlights-clonar/publisher-highlights-clonar.html [MODIFY]

    • gestor/modulos/publisher-highlights/resources/en/pages/publisher-highlights-adicionar/publisher-highlights-adicionar.html [MODIFY]

    • gestor/modulos/publisher-highlights/resources/en/pages/publisher-highlights-editar/publisher-highlights-editar.html [MODIFY]

    • gestor/modulos/publisher-highlights/resources/en/pages/publisher-highlights-clonar/publisher-highlights-clonar.html [MODIFY]

  • Alteração: Substituir o elemento <select id="selected_items" name="selected_items" ...> antigo por uma nova estrutura baseada em input de busca básico e um contêiner de tags (labels) reordenáveis:

    <!-- Nova estrutura de busca autocomplete -->
    <div class="ui fluid icon input search-autocomplete-wrapper" style="position: relative;">
      <input type="text" id="manual_search_input" placeholder="Digite para buscar publicações..." autocomplete="off">
      <i class="search icon"></i>
      <!-- Lista flutuante de sugestões (Autocomplete Dropdown) -->
      <div id="search-suggestions-dropdown" class="ui vertical menu" style="display: none; position: absolute; top: 100%; left: 0; width: 100%; z-index: 1000; max-height: 250px; overflow-y: auto; box-shadow: 0 4px 6px rgba(0,0,0,0.15);">
      </div>
    </div>
    <!-- Contêiner de tags selecionadas (reordenáveis) -->
    <div id="selected-labels-container" class="ui segment sortable-labels-container" style="margin-top: 12px; min-height: 50px; padding: 10px; border: 1px dashed #ccc; border-radius: 4px; display: flex; flex-wrap: wrap; gap: 8px; align-items: center; background: #fafafa;">
      <!-- Labels inseridos dinamicamente por JS -->
    </div>

    (Nota: Ajustar os placeholders/avisos para inglês nos arquivos contidos na pasta en/).


2.3 Dependência do Drag-and-Drop (Sortable.js)

  • Inclusão no PHP:

    • No arquivo gestor/modulos/publisher-highlights/publisher-highlights.php, incluir a biblioteca Sortable.js via CDN em publisher_highlights_adicionar(), publisher_highlights_editar() e publisher_highlights_clonar() para garantir que o script esteja carregado em todas as telas de montagem do CRUD.

    • Para isso, o executor deve utilizar a função global do sistema gestor_pagina_javascript_incluir (definida em gestor.php). Como a função aceita strings HTML cruas e as empilha no array global de scripts se o primeiro parâmetro não for 'biblioteca', basta invocar:

      gestor_pagina_javascript_incluir('<script src="[REDACTED]></script>');

2.4 Lógica de Controle JavaScript

  • Local: gestor/modulos/publisher-highlights/publisher-highlights.js [MODIFY]

  • Alterações principais:

    1. Remover referências antigas: Eliminar instanciamento, chamadas de métodos, e referências a manualItemsDropdown e ensureManualItemsDropdown().

    2. Inicialização/Hidratação:

      • Ao carregar a página, se schema.selected_items contiver slugs (IDs das publicações) e houver um publisher_id selecionado, fazer uma requisição AJAX para buscar os nomes reais dessas publicações.

      • Rota AJAX a utilizar: publisher-pages-fetch (já existente no backend). Parâmetros a enviar no payload data:

      • opcao: gestor.moduloOpcao

      • ajax: 'sim'

      • ajaxOpcao: 'publisher-pages-fetch'

      • params: { publisher_id: publisherId, ids: schema.selected_items }

      • Ao retornar { status: 'Ok', results: [...] }, renderizar as tags dentro de #selected-labels-container respeitando estritamente a ordem contida em schema.selected_items.

    3. Interface dos Labels (Tags):

      • Cada label inserido no contêiner deve possuir o formato de label do Fomantic UI com ícone de mover e botão de remover:

        <div class="ui label teal drag-label" data-id="SLUG_DA_PAGINA" style="cursor: grab; display: inline-flex; align-items: center; user-select: none;">
        <i class="grip vertical icon" style="margin-right: 6px; opacity: 0.6;"></i>
        NOME_DA_PAGINA
        <i class="delete icon remove-tag-btn" style="margin-left: 8px; cursor: pointer;"></i>
        </div>
    4. Comportamento do Campo de Busca (Autocomplete):

      • Escutar evento de input no campo #manual_search_input.

      • Aplicar um debounce de 300ms (para evitar requisições AJAX excessivas a cada caractere).

      • Se o campo possuir texto, realizar requisição AJAX publisher-pages-search enviando:

      • opcao: gestor.moduloOpcao

      • ajax: 'sim'

      • ajaxOpcao: 'publisher-pages-search'

      • publisher_id: publisherId

      • q: texto digitado

      • Ao retornar { status: 'Ok', results: [...] }, exibir a lista #search-suggestions-dropdown.

      • Cada linha retornada deve ser mapeada em uma div clicável (.item). Itens que já estão selecionados em schema.selected_items devem ser exibidos visualmente desabilitados (ou ignorados) e não permitir nova adição.

      • Seleção de Item: Ao clicar em uma sugestão, adicionar o slug ao final do array schema.selected_items, renderizar a nova tag correspondente no contêiner, limpar o campo de busca, ocultar a lista de sugestões, e atualizar o preview ao vivo (scheduleWidgetPreview(false)).

      • Remoção de Item: Ao clicar no ícone .remove-tag-btn de uma tag, removê-la do DOM, removê-la do array schema.selected_items e disparar scheduleWidgetPreview(false).

      • Fechamento do Dropdown: Ocultar a lista flutuante se o usuário clicar fora do input/dropdown ou pressionar a tecla Esc.

    5. Integração com Drag-and-Drop (Sortable.js):

      • Inicializar o Sortable no contêiner #selected-labels-container se a biblioteca global Sortable estiver disponível:

        if (typeof Sortable !== 'undefined') {
        new Sortable(document.getElementById('selected-labels-container'), {
           animation: 150,
           handle: '.grip.vertical.icon',
           onEnd: function() {
               // Relê a ordem física das tags no DOM e atualiza o schema
               var newOrder = [];
               $('#selected-labels-container .drag-label').each(function() {
                   var id = $(this).data('id');
                   if (id) newOrder.push(id);
               });
               schema.selected_items = newOrder;
               scheduleWidgetPreview(false);
           }
        });
        }
    6. Serialização e Envio do Formulário:

      • No interceptador do submit do formulário ($('.ui.form').on('submit', ...)), certificar-se de serializar o schema atualizado como string JSON dentro do campo oculto fields_schema.


2.5 Correção do CodeMirror do Widget (hep-widget-code)

  • Local: gestor/modulos/publisher-highlights/publisher-highlights.js [MODIFY]

  • Alterações:

    • Reintroduzir o caso 'hep-widget' no handler de abas contentHighlightsTabHandler(tabID):

      case 'hep-widget':
      updateWidgetCodeTab();
      break;
    • Reintroduzir e corrigir a função updateWidgetCodeTab() ao final do arquivo:

      1. Verificação de Lib: Adicionar verificação se CodeMirror está no escopo global; se não, agendar nova tentativa via setTimeout(..., 100).

      2. Evitar Duplicação (Dedup): Antes de instanciar o CodeMirror, verificar se já existe um contêiner .CodeMirror irmão adjacente à textarea #hep-widget-code no DOM:

        
        var $textarea = $('#hep-widget-code');
        if ($textarea.length === 0) return;

      var existingWrapper = $textarea.next('.CodeMirror'); if (existingWrapper.length > 0) { widgetCodeMirror = existingWrapper[0].CodeMirror; }

      3. **Instanciação com Configurações Uniformes**: Se não houver instância, criar usando as opções idênticas ao editor principal e ajustar a altura de 800px:
      ```javascript
      if (!widgetCodeMirror) {
         widgetCodeMirror = CodeMirror.fromTextArea($textarea.get(0), {
             mode: 'htmlmixed',
             htmlMode: true,
             readOnly: true,
             lineNumbers: true,
             lineWrapping: true,
             theme: 'tomorrow-night-bright',
             indentUnit: 4
         });
         widgetCodeMirror.setSize('100%', 800);
      }
      1. Envelopamento do Código:

        • Pegar o slug do destaque através da variável gestor.moduloRegistroId. Se ausente, definir o placeholder conforme o idioma ('[slug-do-destaque]' / '[highlight-slug]').

        • Obter o código HTML do editor visual invocando window.html_editor_get_html().

        • Formatar o conteúdo como:

          <!-- widgets#publisher-highlights->render({"grupo_slug": "SLUG"}) < -->
          HTML_DO_EDITOR
          <!-- widgets#publisher-highlights->render({"grupo_slug": "SLUG"}) > -->
        • Aplicar o valor via widgetCodeMirror.getDoc().setValue(...) e chamar widgetCodeMirror.refresh().


3. Restrições e Versionamento

  • IMPORTANTE: NÃO realizar commits (git commit) ou envios (git push) de forma automática ou autônoma sob nenhuma circunstância. As alterações efetuadas pelo executor devem permanecer em estado de modificação local não commitada para revisã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