Conn2Flow
Navegação das docs

SDD

Decision Log (Archive: DEC-071 to DEC-080)

Alinhamento Visual do CRUD e Painel de Conteúdo de Formulários com Menus (req-068 / BATCH-068). Decisões desta rodada: 1. Unificação Visual: Para harmonizar a interface…

DEC-071 - 2026-06-29 - accepted

Alinhamento Visual do CRUD e Painel de Conteúdo de Formulários com Menus (req-068 / BATCH-068). Decisões desta rodada:

  1. Unificação Visual: Para harmonizar a interface administrativa de formulários com o módulo de menus, a aba principal "Template" foi removida dos 6 HTMLs de CRUD (adicionar/editar/clonar em pt-br/en). O seletor de modelo (#template_id) e a seção de abas do editor/prévia/widget foram movidos para a raiz da página sob o cabeçalho <h4 class="ui dividing header">Conteúdo do Formulário</h4> (en: Form Content).

  2. Prevenção de Conflito de Abas via Contexto: Com a coexistência de dois menus de abas na mesma página (menuForms e menuConteudoForm), a inicialização padrão de abas do Fomantic-UI entrava em conflito, ocultando segmentos incorretos. A decisão técnica para isolar os escopos foi envolver os blocos no HTML em containers dedicados (.forms-main-tabs e .forms-content-tabs) e configurar a inicialização no forms.js passando a propriedade context apontando para esses seletores.

  3. Persistência de Aba Ativa: As abas do conteúdo do formulário passaram a persistir a aba ativa sob a chave tabFormContentActive (no localStorage do respectivo módulo), disparando a sincronização correta ao carregar a página e integrando-se com window.contentPageTabHandler() para sincronizar os CodeMirrors.

DEC-072 - 2026-06-29 - accepted

Integração do Módulo de Formulários com o Controlador de Submissão da Biblioteca de Formulários e Suporte a Múltiplos Formulários Concorrentes (req-069 / BATCH-069). Decisões desta rodada:

  1. formulario_controlador() aceita string OU array de IDs: a função normaliza $formId em $formIds (is_array($formId) ? array_values($formId) : [$formId]) e processa a consulta na tabela forms, a verificação de reCAPTCHA, o controle de acesso (formulario_acesso_verificar) e a preparação de parâmetros num foreach ($formIds as $fid), com reset explícito das variáveis condicionais por iteração (evita vazamento de config entre formulários). A extração do componente form-ui (prompts/ui-texts/ui-components/block-wrapper) é feita uma única vez antes do laço. A query usa banco_escape_field($fid) (hardening; o controlador original interpolava o ID cru).

  2. Injeção indexada por ID + retrocompat plana: as configurações de cada formulário são armazenadas em $forms_js_vars[$fid] e injetadas com gestor_js_variavel_incluir('form', $forms_js_vars) → gestor.form[fid]. Como gestor_js_variavel_incluir faz array_merge_recursive, múltiplas chamadas com IDs distintos (dois widgets na mesma página) acumulam chaves indexadas distintas sem colisão. Quando a chamada usa ID único (string), uma segunda injeção replica as variáveis em formato plano no mesmo namespace para preservar a retrocompatibilidade do controlador legado. Versão da biblioteca biblioteca-formulario bumpada 1.1.0 → 1.2.0 (cache-bust do formulario.js).

  3. Resolução de instância no JS por data-form-id (divergência do intake): o formulario.js passa a iterar sobre $('._forms-submissions-controller, .conn2flow-form') e resolve a config de cada <form> lendo o ID por form.attr('id') || form.attr('data-form-id'); se o ID existir como chave em gestor.form, usa a config isolada (gestor.form[formId]), caso contrário faz fallback ao objeto plano. A prosa do req-069 §2 indicava form.attr('id'), mas todos os templates (clássico contact.html e os 5 de widget) usam data-form-id e nenhum define id no <form> — seguiu-se o código real (precedente "seguir o código, não a prosa do intake"). showError passou a inserir o erro na própria instância (form.prepend) em vez do seletor global ._forms-submissions-controller, isolando mensagens por formulário.

  4. Widget delega à biblioteca e remove o script redundante: forms_render() (forms.widget.php) substitui a inclusão de forms.widget.js por gestor_incluir_biblioteca('formulario') + formulario_controlador(['formId' => $registro['id']]), herdando honeypot, reCAPTCHA v2/v3, rate limiting por IP, validações e mensagens centralizadas do backend. O arquivo gestor/modulos/forms/forms.widget.js foi deletado (sem referências remanescentes no manifesto/código). O gestor_pagina_recursos_incluir() (CSS/head) e o forms_widget_render_inline() foram preservados.

DEC-073 - 2026-06-30 - accepted

Parametrização de Scripts de Widget no Editor HTML e Dinamização do Mapeamento do Módulo Subscriptions (req-070 / BATCH-070). Três frentes (conn2flow + [REDACTED]):

  1. widget_js_include parametriza a injeção de scripts de widget no preview do Editor HTML (conn2flow): html_editor_componente() aceita a chave 'widget_js_include' e a injeta em gestor.html_editor.widget_js_include. No frontend, WIDGET_SCRIPT_MODULES passa a lê-la dinamicamente (fallback aos 4 módulos do core quando ausente). Mudança-chave em montarWidgetAssetsHead: quando widget_js_include é explicitamente informado pelo backend, os scripts daqueles módulos são injetados mesmo sem assinatura [[widgets#...]] presente — necessário porque o conteúdo do preview do editor de forms É o próprio .conn2flow-form (não um wrapper de widget). Editores de página/layout/componente não informam a chave (injetada como null) e mantêm a injeção apenas por assinatura (retrocompatibilidade). Cada módulo passa o seu: forms ['forms'=>true], menus ['menus'=>true], galleries ['galleries'=>true], publisher-index ['publisher-index'=>true].

  2. Recriação do forms.widget.js para o preview, via AJAX dedicado (conn2flow): como formulario_controlador injeta gestor.form[id] apenas no page load do site real (não no iframe srcdoc estático do preview), o preview do editor de forms ficou sem as variáveis dinâmicas após a remoção do forms.widget.js no BATCH-069. O novo forms.widget.js roda dentro do iframe, varre .conn2flow-form/._forms-submissions-controller, recupera as variáveis via rota AJAX forms-render-editor-html (no switch de forms.php), popula window.gestor.form[formId] e carrega assíncronamente interface.js + formulario.js para ligar a submissão. Reuso sem duplicação: o laço de montagem das variáveis por formulário foi extraído de formulario_controlador para um builder puro formulario_montar_js_vars($formIds, $formAjaxOpcao) em formulario.php, consumido tanto pelo controlador (site real) quanto por forms_render_editor_html() (forms.widget.php, preview) — evitando ~170 linhas duplicadas e mantendo a estrutura idêntica de gestor.form[id].

  3. Dinamização de Planos de Cadastro no módulo Subscriptions ([REDACTED]): o mapeamento formulário↔plano e as regras de signup deixam de ser hardcoded ($formToPlan em subscriptions.ajax.public.php, $planMapping em subscriptions.php) e migram para a tabela subscriptions_plans (migração 20260710120000: colunas is_signup, signup_field_email, signup_field_password, signup_field_password_confirm, signup_id_usuarios_perfis). O CRUD de planos ganha checkbox "Plano de Cadastro", autocomplete de perfil (data-target="perfis" → endpoint buscar-perfis) e 3 selects de mapeamento de campos populados pelo endpoint buscar-form-campos; o JS alterna o container e pré-seleciona o mapeamento salvo. No checkout (subscriptions_checkout), o form_id vem de subscriptions_plans por slug+idioma (mantida só a normalização de aliases pt-br aceleracao/mentoria/personalizado). No processamento (subscriptions_ajax_forms_process), is_signup e os nomes dos campos de e-mail/senha vêm do plano vinculado ao formulário; subscriptions_criar_usuario recebe id_usuarios_perfis direto do plano (signup_id_usuarios_perfis), com fallback ao slug quando nulo. Migração com colunas default 0/null: planos previamente de signup precisam ter is_signup ativado e os campos mapeados pelo administrador (passo runtime).

DEC-074 - 2026-06-30 - accepted

Novos Tipos de Campos, Limites via Campo "Opções" e Melhoria Progressiva no Módulo de Formulários (req-071 / BATCH-071). Decisões desta rodada:

  1. Tipos password/date/url/hidden reaproveitam o bloco type-input sem novos templates: o item#type do renderer já emite o tipo nativo para qualquer tipo que não seja select/textarea/radio/checkbox, então os 4 novos tipos caem automaticamente no <input type="..."> existente. O construtor (forms.js) adiciona as 4 opções ao dropdown e expande a textarea de "Opções" (helper typeUsesOptions) para também aceitar hidden/text/textarea/number/date, com placeholders dinâmicos por tipo (optionsPlaceholder).

  2. Reuso do campo "Opções" para limites/diretivas, sem novas colunas: linhas min:X/max:Y/step:Z (parser forms_widget_parse_limits no widget e formulario_parse_limits na biblioteca) configuram minlength/maxlength (text/textarea), min/max/step (number) e min/max (date). Para hidden, o texto de "Opções" é o valor padrão constante. fields_schema permanece o contrato único (sem migração).

  3. Injeção de atributos em PHP em vez de novos placeholders nos templates (divergência do intake §2.1): o req sugeria o placeholder [[item#value]] e "adicionar atributos". Para robustez com formulários legados (cujo HTML salvo no banco não conteria placeholders novos), os atributos extras são injetados diretamente na tag renderizada (forms_widget_inject_tag_attrs/forms_widget_add_tag_class) e o value do hidden via forms_widget_inject_value_if_absent; o placeholder [[item#value]] continua sendo resolvido quando presente. Segue o precedente "seguir o código/robustez, não a prosa do intake". Decisão extra: campos hidden renderizam apenas a <input type="hidden"> (descarta label/wrapper), evitando linha visual vazia com rótulo.

  4. Toggle de senha Vanilla + Melhoria Progressiva (Progressive Enhancement): password é envolto em .forms-password-wrapper com botão .forms-password-toggle (ícone eye), com estilos inline para funcionar em Tailwind/Fomantic/CSS puro sem depender do framework. No formulario.js, o date picker Fomantic (.forms-date-picker) e o dropdown Fomantic só são ativados se $.fn.calendar/$.fn.dropdown existirem (caso contrário, <input type="date">/<select> nativos); o toggle de senha é listener delegado vanilla. As validações de limite são duplicadas no cliente (validateField) e no servidor (formulario_processador), sendo o backend a autoridade.

  5. Mensagens i18n no componente form-ui, com fallback: novos prompts (client) e ajax-messages (server) adicionados em pt-br/en (prompt-min-length/-max-length/-min-value/-max-value/-min-date/-max-date/-url e os respectivos ajax-message-*, incluindo ajax-message-max-length que faltava e ajax-message-min-length agora genérico com #min#). PHP e JS usam o valor do componente quando presente e caem em literais quando vazio (pré-deploy). O componente é populado no banco pela esteira (Update => Core), então as mensagens novas dependem do deploy (passo runtime); os fallbacks cobrem o intervalo.

  6. Cobertura de teste: novo tests/Unit/PHP/FormsWidgetFieldTypesTest.php (9 testes, 32 assertions) blinda as funções puras do renderer (parse de limites, valor do hidden input-only, injeção de minlength/maxlength/min/max/step, classe forms-date-picker, type="url", wrapper/toggle de senha), sem banco. Versões cache-bust: módulo forms 1.0.0→1.1.0 e biblioteca formulario 1.2.0→1.3.0.

DEC-075 - 2026-06-30 - accepted

Remoção do Campo e Metadado "hosting_plan" no Módulo de Planos de Assinatura (req-072 / BATCH-072). Decisões desta rodada:

  1. Remoção da coluna por migração reversível: Criada a nova migração Phinx 20260710130000_remove_hosting_plan_from_subscriptions_plans.php para remover a coluna hosting_plan da tabela subscriptions_plans com segurança, incluindo o guard hasColumn no up() e recriação no down().

  2. Saneamento do CRUD: O controlador subscriptions-plans.php e os 4 templates HTML de adicionar/editar planos (pt-br/en) foram limpos para remover a captura, validação, atribuição, placeholders e exibição visual do campo.

  3. Saneamento do Hooks de Hidratação: A função subscriptions_hidratar_config_bd em subscriptions.hooks.php foi atualizada para remover hosting_plan da consulta do banco de dados na tabela subscriptions_plans e remover a injeção condicional da chave no array $planEntry.

DEC-076 - 2026-06-30 - accepted

Parametrização Dinâmica de E-mails e Integração de Estágios de Serviço em Planos (req-073 / BATCH-073). Decisões desta rodada:

  1. Dinamização de E-mail de Proposta/Pedido: Em vez de carregar templates e assuntos de e-mail estáticos com base no formId (por exemplo, form-pro-custom), a função subscriptions_ajax_forms_process() no checkout lê message_component e subject diretamente de fields_schema->email com fallbacks. Os placeholders #name#, #code#, #formName# são substituídos dinamicamente no assunto configurado.

  2. Status Inicial do E-mail por Flag: Adicionada a flag is_free em subscriptions_plans. O status do pedido exibido no e-mail de confirmação é dinâmico: se is_free for verdadeiro, mostra "Ativo - Plano Gratuito" (verde); caso contrário, "Novo - Aguardando Pagamento" (azul).

  3. Autocomplete de Componentes de Mensagem no Forms: O campo email_message_component no construtor de formulários virou autocomplete AJAX buscando componentes na tabela componentes (via novo endpoint buscar-componentes em forms.php). O JS de autocomplete no admin foi integrado no forms.js, sincronizando as alterações com o fields_schema.

  4. Reorganização do CRUD e Carga Dinâmica de Estágio Inicial: O formulário de planos de assinatura foi segmentado nos templates HTML em blocos lógicos (Geral, Integração, Serviço, Pagamentos / Gateways, Mapeamento de Cadastro). O service_stage_type e o estágio inicial initial_stage são integrados no banco de dados e interface. Ao selecionar o tipo de serviço no admin, o JS dispara uma chamada AJAX ao novo endpoint buscar-estagios em subscriptions-plans.php para repopular dinamicamente as opções de estágio inicial com os estágios de serviço correspondentes da tabela subscriptions_service_stages.

DEC-077 - 2026-06-30 - accepted

Dinamização Completa de Planos e Saneamento de Hardcodes no Módulo Subscriptions (req-074 / BATCH-074). Duas frentes ([REDACTED] + conn2flow):

  1. Nome do plano via banco, sem dicionários ([REDACTED] §1): novo helper subscriptions_obter_nome_plano($slug, $lang) em subscriptions.hooks.php consulta subscriptions_plans.name por id+language (status≠'D'), substituindo os 5 dicionários estáticos $paypalPlanNames/$planDisplayNames/$planNames (2 nos hooks, 3 no controlador). Divergência do intake: o fallback é ucfirst(str_replace(['-','_'],' ',$slug)) (humaniza cloud-nano→Cloud Nano) em vez do ucfirst($slug) literal — preserva o comportamento já existente em um dos sites.

  2. Filtro de submissões por planos ativos, sem prefixo chumbado ([REDACTED] §2): helpers subscriptions_obter_form_ids_planos() (lista de form_id distintos de planos status='A') e subscriptions_obter_form_ids_clausula($coluna='form_id') (gera form_id IN ('a','b',...) escapado, com fallback LIKE 'form-pro-%' quando o banco ainda não tem planos — retrocompatibilidade). Aplicados nos 3 filtros de webhook/IPN (2 hooks + 1 controlador) que antes usavam form_id LIKE 'form-pro-%'.

  3. Checkout dinâmico e agnóstico ao plano ([REDACTED] §3 + §7): subscriptions_checkout() resolve o plano direto em subscriptions_plans por slug (subscriptions_checkout_carregar_plano), com dicionário mínimo de aliases legados (aceleracao→acceleration, mentoria→mentorship, personalizado→custom) antes de marcar inválido — removendo os mapas estáticos $plans/$planCelMap e o loop de 6 blocos. O cabeçalho usa #plan_name#/#plan_description# do banco; o card de preço (#plan_price_card#, helper subscriptions_checkout_price_card) é montado de subscriptions_plans_payments (nota da parcela principal, ou mensagem de gratuito/sob medida). Decisão-chave (orientação do usuário humano): em vez de dois conjuntos fixos de campos (signup/custom) propostos no intake, o formulário do plano é renderizado dinamicamente via forms_render(['form_id'=>...]), que lê o campo forms.html da tabela forms por form_id e injeta o controlador de submissão, o JS público e o CSS — preservando os campos específicos de cada plano e cobrindo qualquer plano futuro sem tocar no código. O subscription-checkout.html (pt-br/en) caiu de ~990 para ~135 linhas, mantendo os steps step_form/step_payment/step_success e valid_plan/invalid_plan. O gate de criação de pagamento PayPal passou de activePlanCel !== 'custom' para total_payments > 0.

  4. Landing e callback enxutos ([REDACTED] §4 + §5): subscription.html (pt-br/en) reduzido (~690→~55 linhas) removendo o marketing herdado do antigo pro-manager (Hero/Filosofia/VPS/“Por que pagar”/FAQ), preservando os marcadores que o PHP consome (custom_layout/plans_grid/plan_card + placeholders). subscription-start.html (pt-br/en) teve os textos saneados, trocando jargão específico (“hospedagem”, “cloud hosting”, “seu projeto”, “our subscription platform”) por linguagem neutra de assinatura, sem alterar marcadores de status nem placeholders dinâmicos (#plan_display_name#/#plan_name#/#customer_name#).

  5. Proteção de campos password em submissões (conn2flow §6): em gestor/bibliotecas/formulario.php, ao montar $fieldsValues para gravar forms_submissions.fields_values, valores de campos type === 'password' são gravados como '' (chave preservada por integridade estrutural). Extensão de defesa em profundidade: o mesmo campo é omitido (continue) no laço que monta o e-mail de notificação/confirmação, evitando vazar a credencial por e-mail. O valor em texto plano permanece em $_POST durante a requisição para o hook/controller de signup criar/vincular o usuário. Sem teste unitário dedicado (guard de 1 linha embutido no formulario_processador, acoplado a banco/POST/reCAPTCHA — segue o precedente de DEC-072/DEC-073 de slices de fluxo não isoláveis); validado por php -l + suíte composer test 76/76 sem regressão.

DEC-078 - 2026-07-09 - accepted

Dashboard Site Toolbar, Agendamento de Páginas e Extensões do Editor HTML (req-075 / BATCH-075). Lote consolidado de 6 metas, implementado em slices sequenciais sobre esqueletos scaffoldados pelo operador. Decisões desta rodada:

  1. Botão "Acessar Site" (Meta 1): âncora #menu-site-btn (ícone external alternate, href="@[[pagina#url-raiz]]@", tooltip i18n) no grupo ui icon buttons da .menu-controls do layout admin (pt-br/en). O operador recoloriu para ui green button.

  2. Dashboard Site Toolbar em iframe (Meta 2): rota dashboard-site-toolbar/ (layout layout-pagina-simples, Tailwind), controlador dashboard_site_toolbar()/_menu(), e injeção gestor_dashboard_toolbar() no fim do roteador de páginas de gestor.php — gate único cacheado gestor_dashboard_toolbar_ativo() (editor logado, permissão editar/admin-paginas, página pública, não-iframe, não o próprio toolbar), reusado pela injeção E pelo wrapper de conteúdo. Iframe fixo 30px + dashboard.toolbar.js incluído via gestor_pagina_javascript_incluir(tipo=toolbar, modulo_id=dashboard) (chamado ANTES do flush de javascript-fim). UX (aval do Chefe): menu de módulos e barra de edição redimensionam o iframe via c2f-toolbar:resize (padrão do seletor de linguagens em global.js), com transparência; a barra de edição EMPURRA a página (offset dinâmico 30↔74px), o dropdown apenas sobrepõe.

  3. Redirect avançado inteligente (Meta 4): admin-paginas e publisher-pages editam a tabela paginas keyed por id; divergência do intake — o publicador precisa TAMBÉM do publisher_id (distinto do id da página) para fixar o contexto: publisher-pages/editar/?id=<paginas.id>&publisher_id=<paginas.publisher_id>; senão admin-paginas/editar/?id=<paginas.id>.

  4. Edição visual in-place marker-based (Meta 3, aval do Chefe): o editor mostra a página RENDERIZADA (fiel), mas cada conteúdo vindo de @[[var]]@/widget é envolvido numa caixa de destaque protegida (.c2f-dyn-box, data-c2f-marker = base64 do marcador). O usuário edita tudo menos as caixas; no salvar, a reconstrução é determinística (caixa→marcador, resto→HTML editado), sem diff difuso — preservando vars/widgets sem "queimar" o dinamismo. Endpoints AJAX dashboard site-toolbar-render/site-toolbar-save; wrapper #c2f-page-content (mesmo gate); frontend contenteditable in-place + reload. Não se edita o DOM renderizado diretamente (queimaria o dinamismo); não se usa o overlay de editor separado (revela controles abaixo da toolbar).

  5. Agendamento e datas retroativas (Meta 5): migração 20260712100000 adiciona data_publicacao_inicio/_fim (datetime null) a paginas. Regra 404 no roteador (gestor.php): a query de página ativa (principal + fallback de idioma, não a de idiomas disponíveis) filtra NOW() dentro da janela — fora = 404; páginas de sistema (janela NULL) passam. Helpers globais de data (gestor_parse_datetime_br/gestor_datetime_para_input). CRUD de admin-paginas e publisher-pages (adicionar/clonar/editar) grava janela + data_criacao/data_modificacao retroativas; 12 forms ganham a seção "Agendamento e datas" (datetime-local). Limitações registradas (sem pré-preenchimento no editar; alteração só-de-agendamento pode não disparar save).

  6. Atalho de widget no editor HTML (Meta 6): botão he-tb-widget-admin no #html-editor-floating-toolbar (html-editor.js), visível só com um .conn2flow-widget-wrapper selecionado, abrindo <raiz><data-widget-type>/editar/?id=<data-widget-slug> em nova aba. Cache-bust biblioteca-html-editor 1.3.27→1.3.28.

Validação: php -l (6 arquivos) OK, node --check (2 JS) OK, composer test 76/76 (287 assertions, 4 skipped) sem regressão. Runtime pendente com o operador (deploy Update => Core + migração + validação visual). BATCH-076 (req-076) é paralelo e fora deste escopo. Nenhum git commit/git push executado.

Adendo — Rodada de correções (2026-07-09, feedback do Chefe pós-teste) (Slice 5 no VALIDATION-CHECKLIST). Revisões sobre os pontos acima:

  • Ponto 4 (Meta 3) — editor visual REAL, não contenteditable: o contenteditable in-place foi substituído pela reutilização do editor visual admin. O html-editor.js aceita contentRoot (default document.body, mantendo o clássico intacto), com auto-init guardado por window.__c2fHtmlEditorNoAutoInit e window.HtmlEditorClass exposto. O dashboard.toolbar.js instancia new HtmlEditorClass({contentRoot:#c2f-page-content}) — caixas tracejadas de seleção, floating toolbar, styler, DnD reais. Modal fallback vanilla (ensureFallbackModal) e imagepick por window.prompt quando não há Fomantic/config. CodeMirror inicializado in-place no #element-code.

  • Ponto 5 (Meta 5) — datas via formato.php: os helpers globais gestor_parse_datetime_br/gestor_datetime_para_input foram removidos do gestor.php e substituídos por formato_data_hora_br_para_datetime() (aceita BR e ISO — datetime-local mantido) e formato_data_hora_datetime_para_input() na biblioteca formato.php.

  • Novo — edição do LAYOUT (via layout_id): além do conteúdo (paginas), o save edita o body-inner do registro layouts do banco (dashboard_site_toolbar_salvar_layout), com o #c2f-page-content recolocado como @[[pagina#corpo]]@ (safeguard exige o marcador) e vars de atributo mantidas literais ($preservar_atributos=true). Só o body-inner (head não).

  • Novo — histórico/backup + restauração: o save grava interface_backup_campo_incluir + interface_historico_incluir no módulo-dono; o editbar ganhou dropdown de restauração (#c2f-backups-btn → site-toolbar-backups/site-toolbar-backup-get → openBackupPanel).

  • Novo — user_modified=1: o save marca user_modified=1 (+versao+1) em paginas e em layouts (quando o layout muda no mesmo salvamento).

  • Novo — controles no editbar + painel de widgets: screenPagina (desktop/tablet/mobile), undo/redo e add-btn com painel de inserção de elementos e widgets (endpoints site-toolbar-widget-types/site-toolbar-widgets-list reusando html_editor_ajax_widget_types/_widgets_list).

Validação do adendo: node --check dashboard.toolbar.js OK; php -l (dashboard.php, gestor.php, formato.php) OK; composer test 76/76 (287 assertions, 4 skipped) sem regressão. Cache-bust biblioteca-html-editor até 1.3.30. Nenhum git commit/git push executado.

DEC-079 - 2026-07-10 - accepted

Desacoplamento de Scripts do Iframe e Mapeamento Inteligente de Variáveis no Live Editor (req-077 / BATCH-077). Correções pós-teste visual do BATCH-075. Decisões desta rodada:

  1. Script da toolbar como arquivo estático (não inline): a lógica JS embutida no <script> do template dashboard-site-toolbar.html (pt-br/en) foi extraída para gestor/modulos/dashboard/dashboard.iframe-toolbar.js e passou a ser injetada por gestor_pagina_javascript_incluir(Array('tipo'=>'iframe-toolbar','modulo_id'=>'dashboard')) em dashboard_site_toolbar(). O separador módulo→tipo do incluidor é o ponto (dashboard + iframe-toolbar → URL dashboard/iframe-toolbar.js), e o hífen dentro do tipo (iframe-toolbar) casa a regex /^[A-Za-z0-9-]+$/ do roteador arquivo-estatico.php, que mapeia ao físico dashboard.iframe-toolbar.js. Um único arquivo serve pt-br e en (o JS não tem strings i18n — os rótulos ficam no HTML). O placeholder órfão vazio dashboard.iframe.toolbar.js (0 bytes, sem referências) foi removido.

  2. Preservação do DOM VIVO em vez de swap destrutivo de innerHTML (mudança-chave): o motor do BATCH-075 substituía root.innerHTML pelo HTML re-renderizado com caixas, o que quebrava scripts ativos, CSS e estado JS do site. O motor novo não toca o DOM vivo: guarda o HTML ORIGINAL (cru, com marcadores intactos) num container oculto #paginaHTMLAntesEdicao e anota a árvore viva comparando-a com a original (mapTree, varredura paralela alinhada por assinatura estrutural). O backend dashboard_ajax_site_toolbar_render passou a devolver content_raw (=paginas.html) e layout_raw (=body-inner do layout com o slot @[[pagina#corpo]]@ preenchido por #c2f-raw-content, espelhando a estrutura viva #c2f-layout-root⊃#c2f-page-content); os campos legados com caixas (html/layout_html) foram mantidos para retrocompat (painel de backups).

  3. Mapeamento por tipo de ocorrência da variável:

    • Atributo (caso de aceite: @[[pagina#url-raiz]]@ em src/href): a tag viva é marcada com data-c2f-variable="ID_VAR_N" e o mapa varMap[ID]={param, variable(template cru do atributo), valor(resolvido)} guarda a correspondência. O valor resolvido continua visível no editor (visual correto); no save, o atributo volta à notação @[[…]]@ só quando o valor não foi alterado pelo usuário (senão a edição prevalece). Isso resolve o dilema do motor antigo, que no modo conteúdo queimava as variáveis de atributo no banco.

    • Texto: annotateTextVars deriva os valores por regex do template cru e envolve cada variável num span.c2f-var-box (contenteditable=false) com o marcador em base64 (data-c2f-marker).

    • Widget: como o fluxo vivo (gestor_pagina_widgets) substitui o bloco <!-- widgets#… --> pelo HTML renderizado sem deixar âncora, mapTree localiza a expansão viva por âncora estrutural (a 1ª assinatura do cru após o bloco de widget) e a envolve num div.c2f-widget-box atômico, guardando o marcador cru completo (open+mockup+close). Best-effort: em páginas muito complexas com múltiplos widgets/expansões o alinhamento pode ser parcial; degrada sem corromper (nós não mapeados seguem editáveis).

  4. Save separa paginas de layouts (orientação do Engenheiro Chefe): o body vivo mistura HTML do registro layouts (body-inner) e do registro paginas (conteúdo). saveEdit/reconstructOriginal extraem #c2f-page-content → grava em paginas; o restante do #c2f-layout-root, com o #c2f-page-content trocado pelo marcador @[[pagina#corpo]]@, → grava em layouts (mantido o mecanismo de save duplo do BATCH-075/adendo P4, agora sobre o DOM vivo anotado).

Validação: node --check (2 JS) OK, php -l (dashboard.php) OK, <div> 5/5 nos 2 HTMLs. Teste de lógica do motor com happy-dom carregando o dashboard.toolbar.js real e exercitando edit-start/edit-save via message-bus: 16/16 (atributos do aceite, variável de texto e widget mapeados/reconstruídos, DOM vivo preservado). Runtime (deploy Update => Core + edição visual real) pendente com o operador. Nenhum git commit/git push executado.

DEC-080 - 2026-07-10 - accepted

Correções no Live Editor: Trava de Widgets, Submenu do Painel "+" e Isolamento de Estilos (req-078 / BATCH-078). Rodada de correções visuais/interação pós-homologação do BATCH-075/BATCH-077, todas pontuais no motor compartilhado html-editor.js (o Live Editor reutiliza o editor visual admin escopado a contentRoot, rodando na própria página hospedeira). Decisões desta rodada:

  1. Widgets/variáveis do live editor são blocos atômicos protegidos (correção 1): no live editor os widgets renderizados são envolvidos por .c2f-widget-box/.c2f-dyn-box (mapeamento do BATCH-077), e não por .conn2flow-widget-wrapper — por isso a proteção atômica do editor clássico não os alcançava e o conteúdo interno seguia editável/selecionável. resolveEditable passa a reconhecer .c2f-dyn-box (element.closest('.c2f-dyn-box') → retorna a caixa), dando paridade com o wrapper clássico; o CSS injetado (injectStyles) adiciona .c2f-dyn-box{user-select:none} e .c2f-dyn-box *{pointer-events:none!important;user-select:none!important}, deixando o conteúdo interno (texto/imagem/links do widget) inerte a clique/seleção. O usuário só interage com a caixa externa. Seguro para o admin (as classes c2f-* não existem lá).

  2. Painéis do live editor entram em isEditorOwned (correção 2 — causa-raiz): #c2f-add-panel não estava listado em isEditorOwned, então o listener de clique em capture do html-editor.js (que usa elementsFromPoint) atravessava o painel, encontrava o conteúdo por trás, selecionava-o e chamava stopPropagation() — matando o toggleWidgetGroup do painel e produzindo o sintoma "a categoria fecha o painel / age como inserção". isEditorOwned passa a reconhecer #c2f-add-panel e #c2f-backup-panel (via closest), dando early-return no clique/hover; o clique propaga normal para o handler do painel (que já fazia o toggle correto no dashboard.toolbar.js). Nenhuma mudança na lógica do painel foi necessária.

  3. Blindagem de CSS dos controles injetados na página hospedeira (correção 3): bloco de hardening em injectStyles forçando com !important color/background/-webkit-text-fill-color/font/caret-color em #html-editor-floating-toolbar .he-tb-btn (+ ícones <i> e o he-tb-deselect rosa preservado), #html-editor-tailwind-styler (+ seus input) e #html-editor-modal textarea,input[type="text"],label. Isso corrige ícones apagados no floating toolbar, textarea/input ilegíveis (texto cinza herdado do site) no modal e o styler desconfigurado. Nota técnica: color:...!important no container do styler não sobrescreve os filhos com cor própria (tags roxas, títulos cinza) porque a herança CSS não propaga !important — apenas os elementos sem cor própria são blindados; o visual intencional é preservado.

  4. Cache-bust duplo: o live carrega interface/html-editor.js?v=c2f1 → ?v=c2f2 (query-string hardcoded no dashboard.toolbar.js); o admin bumpa biblioteca-html-editor 1.3.30 → 1.3.31 (html-editor.php). Como o motor é compartilhado, os dois contextos precisam recarregar.

Validação: node --check (html-editor.js, dashboard.toolbar.js) OK; php -l (html-editor.php, dashboard.php) OK; composer test 76/76 sem regressão.

  • Adendo — Rodada 2 (R2) — Refinamentos Visuais e Funcionais do Live Editor (2026-07-10):

    1. Widgets Sem Wrapper Div (R2-P4): A introdução de uma tag <div> de wrapper (.c2f-widget-box) no BATCH-077 quebrava seletores CSS estritos do site hospedeiro (como .parent > .child). O novo modelo de mapeamento marca diretamente os elementos-raiz do render do widget com os atributos data-c2f-widget-id (grupo), data-c2f-widget-root="1" (primeiro elemento), e data-c2f-marker (marcador cru).

    2. Reconstrução Sem Wrapper: reconstructOriginal agrupa elementos por data-c2f-widget-id, substitui o primeiro elemento com o marcador cru original e exclui os elementos irmãos pertencentes ao mesmo widget. Isso permite o salvamento e restauração determinística sem introduzir nenhuma tag de wrapper no HTML final.

    3. Proteção Interna de Widgets (R2-P1): No resolveEditable, reconhecer elementos com [data-c2f-widget-id] como blocos atômicos protegidos. O CSS injetado desabilita seleção e eventos em elementos filhos ([data-c2f-widget-id] * { pointer-events: none!important; user-select: none!important; }).

    4. Aparência Tracejada e Label (R2-P1): Exibição visual do widget via outline: 1px dashed #fbbf24!important e um label absoluto no pseudo-elemento ::before (exibindo o nome e slug do widget) ancorado no elemento-raiz (position: relative).

    5. Atalho de Widget Unificado (R2-P2): O botão #he-tb-widget-admin na toolbar flutuante verifica se o elemento possui data-widget-type, compatibilizando o atalho para wrappers clássicos e os novos elementos-raiz do live editor.

    6. Ícones SVG Autônomos (R2-P3): As tags <i class="... icon"> (Fomantic UI) da toolbar flutuante e do styler foram substituídas por SVGs inline com stroke="currentColor". Isso garante o funcionamento e exibição correta dos ícones em páginas públicas que não carregam fontes ou folhas de estilo da Fomantic UI.

    7. Cache-Bust: Atualizada a query-string para ?v=c2f3 no carregador de html-editor.js e a versão do componente biblioteca-html-editor para 1.4.0 em html-editor.php.

Validação da Rodada 2:

  • Executado node _smoke_batch078_r2.mjs no ambiente de testes happy-dom validando a round-trip completa (marcação de elementos sem wrapper, preservação de variáveis de atributo no DOM e reconstrução determinística) com 18/18 checks aprovados.

  • node --check (JS) e php -l (PHP) OK.

  • composer test 76/76 aprovados sem regressões.

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