Descrição do Problema / Contexto
Atualmente, a barra de inserção de elementos no Editor HTML Visual (html-editor-add-panel em html-editor-visual-controls.js) possui uma lista estática (hardcoded) de 4 módulos de widgets do sistema (WIDGETS_MODULOS): Destaques, Menus, Galerias e Índice.
Além disso, a verificação e carga de widgets no backend (html-editor.php -> html_editor_ajax_widgets_list()) também é baseada em um mapa estático das tabelas (menus, galleries, publisher_highlights, publisher_index, forms).
Para permitir a extensão dinâmica do sistema por novos módulos que possam introduzir novos widgets, precisamos substituir essa abordagem rígida por um registro dinâmico no banco de dados através de uma tabela global widgets, cuja população será feita de forma automática via esteira de Sincronização de Recursos.
Como o número de widgets ativos pode crescer significativamente, a interface de seleção no frontend deve migrar para um carregamento dinâmico (lazy loading) sob demanda via AJAX conforme o usuário interage (expande) as categorias no menu.
1. Estrutura do Banco de Dados (Phinx Migration)
Criar uma nova migração Phinx (ex: 20260708100000_create_widgets_table.php) para criar a tabela global widgets:
-
Tabela:
widgets -
Chave Primária:
id_widgets(int, auto-incremento) -
Colunas:
-
id(varchar 100, NOT NULL) -> O identificador lógico do widget / slug do módulo (ex:'publisher-highlights') -
name(varchar 255, NOT NULL) -> O rótulo amigável (label) da categoria (ex:'Destaques') -
icon(varchar 100, NOT NULL) -> O ícone CSS correspondente (ex:'star') -
tabela(varchar 100, NOT NULL) -> A tabela de banco de dados onde os registros de widgets desse tipo estão armazenados (ex:'publisher_highlights') -
coluna_where(varchar 100, NULL) -> Opcional. A coluna a ser utilizada como filtro na tabela alvo, caso aquele widget represente apenas um subconjunto (ex:'type'). -
language(varchar 10, NOT NULL, default'pt-br') -
status(char 1, default'A') -
versao(int, default 1) -
data_criacao(datetime, defaultCURRENT_TIMESTAMP) -
data_modificacao(datetime, defaultCURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP) -
user_modified(tinyint, default 0) -
system_updated(tinyint, default 0) -
project(varchar 255, NULL) -> Necessário para integridade de deploy de projetos.
-
-
Índice Único:
['id', 'language'].
2. Registro de Recursos nos Módulos e Configuração Global
2.1 Configuração Global de Sincronização (tables_config.json)
Adicionar a tabela widgets em gestor/resources/tables_config.json sob a chave "tabelas" para que seja reconhecida pela esteira de sincronização de banco de dados (atualizacoes-banco-de-dados.php via schema-metadata.json):
"widgets": {
"nome": "widgets",
"id": "id",
"id_numerico": "id_widgets",
"config": {
"strategy": "natural_key",
"natural_key_columns": [
"language",
"id"
],
"preserve_on_user_modified": [],
"insert_only": false
},
"deletar": []
}2.2 Manifestos JSON dos Módulos
Adicionar a declaração de recursos "widgets" nos arquivos JSON descritores de módulos para alimentar a tabela global.
(Nota: O primeiro registro já foi inserido no publisher-highlights.json pelo Engenheiro Chefe):
-
Publisher Highlights (
gestor/modulos/publisher-highlights/publisher-highlights.json- já existente):"widgets": [ { "id": "publisher-highlights", "name": "Destaques", "icon": "star", "tabela": "publisher_highlights" } ] -
Menus (
gestor/modulos/menus/menus.json- adicionar):"widgets": [ { "id": "menus", "name": "Menus", "icon": "bars", "tabela": "menus" } ] -
Galerias (
gestor/modulos/galleries/galleries.json- adicionar):"widgets": [ { "id": "galleries", "name": "Galerias", "icon": "images", "tabela": "galleries" } ] -
Publisher Index (
gestor/modulos/publisher-index/publisher-index.json- adicionar):"widgets": [ { "id": "publisher-index", "name": "Índice", "icon": "list", "tabela": "publisher_index" } ]
3. Compilador de Recursos (atualizacao-dados-recursos.php)
Ajustar o script gestor/controladores/agents/arquitetura/atualizacao-dados-recursos.php para coletar o novo tipo de recurso widgets de dentro dos módulos:
-
Adicionar
'widgets'à lista de tipos processados no laço de varredura de módulos. -
Se
$tipo === 'widgets', ler a lista do array e salvar o registro estruturado (normalizandonome,id,icon,tabela,coluna_where,language,status= 'A',versao= 1,user_modified= 0) na pilha$widgets. -
Saneamento de duplicidades: Usar índice
$idxWidgets[$key](chavelang|id) e enviar duplicados para o array de órfãos ($orphans['widgets']). -
Salvar a lista consolidada no final gerando o arquivo serializado
gestor/db/data/WidgetsData.jsonpor meio dejsonWrite($DB_DATA_DIR.'WidgetsData.json', ...). -
Incluir o tipo
'Widgets'na lista de limpeza/geração de órfãos no diretóriodb/orphans/.
4. Roteamento e Carga Dinâmica no Backend (html-editor.php)
No arquivo gestor/bibliotecas/html-editor.php:
4.1 Nova Rota: Lista de Categorias/Tipos de Widgets (html-editor-widget-types)
-
Criar a função AJAX
html_editor_ajax_widget_types()(chamada pela rotahtml-editor-widget-types):-
Consultar a tabela
widgetspara o idioma ativo de controle:SELECT id, name, icon FROM widgets WHERE status = 'A' AND language = :idioma ORDER BY name ASC -
Retornar a lista em formato JSON
[ { id: '...', name: '...', icon: '...' }, ... ].
-
4.2 Rota Atualizada: Registros de Widgets sob Demanda (html-editor-widgets-list)
-
Ajustar a função
html_editor_ajax_widgets_list()para receber o parâmetromodule(ouwidget_type) do request. -
Consultar a tabela
widgetspara carregar as definições do widget especificado:-
Query:
SELECT tabela, coluna_where FROM widgets WHERE id = :module AND language = :idioma AND status = 'A' LIMIT 1 -
Se não encontrar, retornar array vazio.
-
-
Executar uma consulta dinâmica na tabela alvo (
tabela):-
Validar se o nome da tabela (
tabela) e o nome da coluna (coluna_where) contêm apenas caracteres seguros (alfanuméricos e underscore) para evitar SQL Injection. -
Montar a cláusula
WHERE: -
Base:
WHERE status = 'A' AND language = :idioma -
Se
coluna_whereestiver preenchido: concatenarAND {coluna_where} = :module -
Query final:
SELECT id, name FROM {tabela} {WHERE} ORDER BY name ASC(Nota: Se a tabela alvo não possuir a colunaname(ou se o identificador for diferente), ler de forma compatível ou fallback paraid). -
Devolver os registros em formato JSON
[ { id: '...', nome: '...' }, ... ].
-
5. Interface e Interação no Frontend (html-editor-visual-controls.js)
No arquivo gestor/assets/interface/html-editor-visual-controls.js:
-
Remover a variável hardcoded
WIDGETS_MODULOS. -
Fluxo de Carga Inicial:
-
Ao carregar/abrir o painel (
abrirPainel()), se a lista de categorias não estiver em cache: -
Realizar uma chamada AJAX para
html-editor-widget-types. -
Renderizar dinamicamente os grupos de categorias de widgets sob a seção "Widgets do Sistema" com o ícone e nome retornados.
-
Cada grupo (
.he-add-widget-group) deve iniciar fechado/colapsado e conter a sublista com o texto "Carregando..." (.he-add-widget-list).
-
-
Fluxo de Expansão sob Demanda (Lazy Loading):
-
Adicionar/ajustar o evento de clique em
.he-add-widget-head. -
Ao expandir uma categoria (
.he-add-widget-group), verificar se os itens dela já foram carregados. -
Se ainda não carregados:
-
Disparar uma chamada AJAX para
html-editor-widgets-listpassando o parâmetromodulecorrespondente. -
Ao receber o retorno, renderizar os itens do widget no DOM (gerando elementos
.he-add-widget-itemcomdata-module,data-slug,data-name) e remover a mensagem de carregamento. -
Salvar os itens carregados em um cache local (
widgetsCache[module] = data) para evitar chamadas AJAX redundantes se o usuário fechar e reabrir a mesma categoria.
-
6. Validação e Testes
-
Verificação de Sintaxe:
-
php -l gestor/controladores/agents/arquitetura/atualizacao-dados-recursos.php -
php -l gestor/bibliotecas/html-editor.php
-
-
Execução da Esteira de Recursos:
-
Executar a compilação local de recursos e verificar se o arquivo
gestor/db/data/WidgetsData.jsoné gerado contendo os widgets dos 4 módulos. -
Rodar a sincronização de banco de dados (
atualizacoes-banco-de-dados.php) e garantir que a tabelawidgetsseja populada.
-
-
Validação da Interface:
-
Abrir o Editor HTML Visual e clicar no botão "+".
-
Confirmar que as categorias (Destaques, Menus, Galerias, Índice) aparecem corretamente como grupos colapsados.
-
Clicar em uma categoria, verificar a chamada AJAX correspondente no Console/Network e constatar que os itens reais são listados somente após a interação.
-