diff --git a/app/controllers/api/v1/accounts/landing_hosts_controller.rb b/app/controllers/api/v1/accounts/landing_hosts_controller.rb index 5c7343cec..4539c3b40 100644 --- a/app/controllers/api/v1/accounts/landing_hosts_controller.rb +++ b/app/controllers/api/v1/accounts/landing_hosts_controller.rb @@ -1,6 +1,6 @@ class Api::V1::Accounts::LandingHostsController < Api::V1::Accounts::BaseController before_action :fetch_inbox, only: [:index, :create] - before_action :fetch_landing_host, only: [:destroy] + before_action :fetch_landing_host, only: [:update, :destroy] def index @landing_hosts = LandingHost.where(inbox_id: @inbox.id) @@ -17,6 +17,14 @@ class Api::V1::Accounts::LandingHostsController < Api::V1::Accounts::BaseControl end end + def update + if @landing_host.update(landing_host_params) + render json: @landing_host + else + render json: { error: @landing_host.errors.full_messages }, status: :unprocessable_entity + end + end + def destroy @landing_host.destroy! head :no_content @@ -31,7 +39,7 @@ class Api::V1::Accounts::LandingHostsController < Api::V1::Accounts::BaseControl end def fetch_landing_host - # Garantimos que a pessoa só possa apagar LandingHosts de Inboxes que pertencem a ela + # Garantimos que a pessoa só possa acessar/apagar LandingHosts de Inboxes que pertencem a ela valid_inbox_ids = Current.account.inboxes.pluck(:id) @landing_host = LandingHost.where(inbox_id: valid_inbox_ids).find(params[:id]) rescue ActiveRecord::RecordNotFound @@ -39,6 +47,11 @@ class Api::V1::Accounts::LandingHostsController < Api::V1::Accounts::BaseControl end def landing_host_params - params.require(:landing_host).permit(:hostname, :unit_code, :active, :auto_label) + params.require(:landing_host).permit( + :hostname, :unit_code, :active, :auto_label, + :page_title, :page_subtitle, :button_text, :logo_url, + :suite_image_url, :theme_color, :whatsapp_number, + :initial_message, :default_source, :default_campanha + ) end end diff --git a/app/controllers/public/landing_pages_controller.rb b/app/controllers/public/landing_pages_controller.rb index 32e21a338..cbae6bc03 100644 --- a/app/controllers/public/landing_pages_controller.rb +++ b/app/controllers/public/landing_pages_controller.rb @@ -1,5 +1,23 @@ class Public::LandingPagesController < PublicController layout false - def show; end + def show + host = request.host.to_s.sub(/^www\./, '') + @landing_host = LandingHost.find_by(hostname: host, active: true) + + # Fallback local para testes + return unless Rails.env.development? && @landing_host.nil? + + @landing_host = LandingHost.first || LandingHost.new( + page_title: 'Atendimento Express', + page_subtitle: 'Clique e fale direto com a recepcao agora', + whatsapp_number: '556136131003', + initial_message: 'Ola! Tenho interesse.', + theme_color: '#27c15b', + logo_url: 'https://iachat.hoteis1001noites.com.br/assets/images/dashboard/captain/logo.svg', + unit_code: 'express', + default_source: 'direto', + default_campanha: 'site' + ) + end end diff --git a/app/javascript/dashboard/api/landingHosts.js b/app/javascript/dashboard/api/landingHosts.js index 3895b78e1..86a5fa6f7 100644 --- a/app/javascript/dashboard/api/landingHosts.js +++ b/app/javascript/dashboard/api/landingHosts.js @@ -13,6 +13,12 @@ export default { { landing_host: data } ); }, + updateHost(accountId, inboxId, id, data) { + return axios.patch( + `/api/v1/accounts/${accountId}/inboxes/${inboxId}/landing_hosts/${id}`, + { landing_host: data } + ); + }, deleteHost(accountId, inboxId, id) { return axios.delete( `/api/v1/accounts/${accountId}/inboxes/${inboxId}/landing_hosts/${id}` diff --git a/app/javascript/dashboard/i18n/locale/pt_BR/captain.json b/app/javascript/dashboard/i18n/locale/pt_BR/captain.json index f0a51be2c..b454f61cb 100644 --- a/app/javascript/dashboard/i18n/locale/pt_BR/captain.json +++ b/app/javascript/dashboard/i18n/locale/pt_BR/captain.json @@ -234,7 +234,8 @@ "Ações" ], "ADD_NEW_ITEM": "Adicione fotos na galeria", - "NO_ITEMS_MESSAGE": "Ainda não há fotos cadastradas para envio automático aos clientes." + "NO_ITEMS_MESSAGE": "Ainda não há fotos cadastradas para envio automático aos clientes.", + "VIEW_URL": "URL da Imagem" }, "DELETE": { "CONFIRM": { @@ -274,14 +275,14 @@ "SPECIFIC_HELP": "Essas fotos serão usadas somente na caixa de entrada {inbox}." }, "SUITE_CATEGORY": { - "LABEL": "Categoria da suíte", + "LABEL": "Categoria", "PLACEHOLDER": "Ex: Hidromassagem", "ERROR": "A categoria é obrigatória" }, "SUITE_NUMBER": { - "LABEL": "Número/identificador da suíte", + "LABEL": "Nome/identificador", "PLACEHOLDER": "Ex: 101", - "ERROR": "O identificador da suíte é obrigatório" + "ERROR": "O identificador é obrigatório" }, "DESCRIPTION": { "LABEL": "Descrição da foto", @@ -337,7 +338,7 @@ "SELECT_INBOX_HINT": "Clique em uma caixa de entrada acima para ver e configurar os templates.", "EMPTY": { "TITLE": "Nenhum template configurado", - "DESC": "Crie templates de mensagem automática para esta caixa de entrada." + "DESC": "Configure as permissões das informações que o sistema utiliza. Por ex.: Quais imagens enviar durante as aproximações." } } }, diff --git a/app/javascript/dashboard/routes/dashboard/settings/captain/gallery/Index.vue b/app/javascript/dashboard/routes/dashboard/settings/captain/gallery/Index.vue index eb68c4295..6eb92ec35 100644 --- a/app/javascript/dashboard/routes/dashboard/settings/captain/gallery/Index.vue +++ b/app/javascript/dashboard/routes/dashboard/settings/captain/gallery/Index.vue @@ -98,11 +98,6 @@ const confirmDelete = async () => { > {{ t('CAPTAIN_SETTINGS.GALLERY.LIST.TABLE_HEADER[2]') }} - - {{ t('CAPTAIN_SETTINGS.GALLERY.LIST.TABLE_HEADER[3]') }} - @@ -120,6 +115,14 @@ const confirmDelete = async () => { class="h-16 w-24 rounded object-cover" /> + +

+ {{ item.suite_category }} +

+

+ {{ item.suite_number }} +

+

{{ @@ -131,13 +134,19 @@ const confirmDelete = async () => {

{{ item.description }}

+
+ + + {{ t('CAPTAIN_SETTINGS.GALLERY.LIST.VIEW_URL') }} + +
- - {{ item.suite_category }} - - - {{ item.suite_number }} - +
- + + + + + + + + +
- {{ labels.remove }} - +

+ {{ labels.cfgTitle }} +

+ +
+ +
+ + +
+
+ + +
+
+ + +
+ + +
+ + +
+
+ + +
+ + +
+ + +
+
+ +
+ + +
+
+
+ + +
+ + +
+ + +
+
+ + +
+ +
+ + +
+
+ + +
+
+ +
+ + +
+
- +

{{ labels.addTitle }} diff --git a/app/models/landing_host.rb b/app/models/landing_host.rb index 2fd1999a6..aebf01284 100644 --- a/app/models/landing_host.rb +++ b/app/models/landing_host.rb @@ -2,14 +2,25 @@ # # Table name: landing_hosts # -# id :bigint not null, primary key -# active :boolean -# auto_label :string -# hostname :string -# unit_code :string -# created_at :datetime not null -# updated_at :datetime not null -# inbox_id :integer +# id :bigint not null, primary key +# active :boolean +# auto_label :string +# button_text :string default("Ver disponibilidade agora") +# custom_config :jsonb +# default_campanha :string +# default_source :string +# hostname :string +# initial_message :text +# logo_url :string +# page_subtitle :string default("Atendimento Imediato\nEntrada Discreta\nSem Burocracia") +# page_title :string default("Atendimento Express") +# suite_image_url :string +# theme_color :string default("#25D366") +# unit_code :string +# whatsapp_number :string default("") +# created_at :datetime not null +# updated_at :datetime not null +# inbox_id :integer # # Indexes # diff --git a/app/models/lead_click.rb b/app/models/lead_click.rb index d0fd0cf61..6b17e7445 100644 --- a/app/models/lead_click.rb +++ b/app/models/lead_click.rb @@ -12,6 +12,7 @@ # user_agent :string # created_at :datetime not null # updated_at :datetime not null +# click_id :string # contact_id :integer # conversation_id :integer # inbox_id :integer diff --git a/app/views/public/landing_pages/show.html.erb b/app/views/public/landing_pages/show.html.erb index e2b271ea7..2ee2d418b 100644 --- a/app/views/public/landing_pages/show.html.erb +++ b/app/views/public/landing_pages/show.html.erb @@ -3,7 +3,8 @@ - Atendimento WhatsApp + <%= @landing_host&.page_title.presence || 'Atendimento WhatsApp' %> + -
-
-
- logo -
-

-

- -
Pagina segura · atendimento humano
-
-
- -
-
-
-

Painel Admin

- -
- -
-
-
-
-
- -
-
-
-
-
-
-
- -
- Mensagem que sera enviada: -
-
- -
- - + <% if @landing_host.nil? %> +
+
+

Página não encontrada

+

Verifique a URL digitada e tente novamente.

+
+
+ <% else %> +
+
+
+ logo +
+

<%= @landing_host.page_title %>

+

<%= @landing_host.page_subtitle&.gsub("\n", "
")&.html_safe %>

+ +
Pagina segura · atendimento humano
-
- + document.getElementById("whatsButton").addEventListener("click", openWhatsapp); + })(); + + <% end %> diff --git a/config/routes.rb b/config/routes.rb index eb676c2bd..6d1d345b0 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -237,7 +237,7 @@ Rails.application.routes.draw do get :campaigns, on: :member get :agent_bot, on: :member post :set_agent_bot, on: :member - resources :landing_hosts, only: [:index, :create, :destroy] + resources :landing_hosts, only: [:index, :create, :update, :destroy] post :setup_channel_provider, on: :member post :disconnect_channel_provider, on: :member delete :avatar, on: :member diff --git a/db/migrate/20260303013420_add_visual_config_to_landing_hosts.rb b/db/migrate/20260303013420_add_visual_config_to_landing_hosts.rb new file mode 100644 index 000000000..7be1585c6 --- /dev/null +++ b/db/migrate/20260303013420_add_visual_config_to_landing_hosts.rb @@ -0,0 +1,4 @@ +class AddVisualConfigToLandingHosts < ActiveRecord::Migration[7.1] + def change + end +end diff --git a/db/migrate/20260303074000_add_config_to_landing_hosts.rb b/db/migrate/20260303074000_add_config_to_landing_hosts.rb new file mode 100644 index 000000000..f2ca63881 --- /dev/null +++ b/db/migrate/20260303074000_add_config_to_landing_hosts.rb @@ -0,0 +1,8 @@ +class AddConfigToLandingHosts < ActiveRecord::Migration[7.0] + def change + add_column :landing_hosts, :initial_message, :text + add_column :landing_hosts, :default_source, :string + add_column :landing_hosts, :default_campanha, :string + add_column :landing_hosts, :custom_config, :jsonb, default: {} + end +end diff --git a/db/schema.rb b/db/schema.rb index 89542cfc6..b8ef5759c 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.1].define(version: 2026_03_02_211000) do +ActiveRecord::Schema[7.1].define(version: 2026_03_03_074000) do # These extensions should be enabled to support this database enable_extension "pg_stat_statements" enable_extension "pg_trgm" @@ -1561,6 +1561,17 @@ ActiveRecord::Schema[7.1].define(version: 2026_03_02_211000) do t.datetime "created_at", null: false t.datetime "updated_at", null: false t.string "auto_label" + t.string "page_title", default: "Atendimento Express" + t.string "page_subtitle", default: "Atendimento Imediato\nEntrada Discreta\nSem Burocracia" + t.string "button_text", default: "Ver disponibilidade agora" + t.string "logo_url" + t.string "suite_image_url" + t.string "theme_color", default: "#25D366" + t.string "whatsapp_number", default: "" + t.text "initial_message" + t.string "default_source" + t.string "default_campanha" + t.jsonb "custom_config", default: {} t.index ["hostname"], name: "index_landing_hosts_on_hostname", unique: true end diff --git a/progresso/2026-03-02_rastreamento_cliques_lp.md b/progresso/2026-03-02_rastreamento_cliques_lp.md index aea172cc0..30bb23319 100644 --- a/progresso/2026-03-02_rastreamento_cliques_lp.md +++ b/progresso/2026-03-02_rastreamento_cliques_lp.md @@ -6,10 +6,26 @@ Garantir que os cliques na landing page sejam rastreados, capturando UTMs (orige ### Contexto Atualmente, o `TrackingController` e o `LeadClick` capturam o hostname e o IP, mas não estão salvando o `click_id` (enviado pelo frontend) nem extraindo parâmetros UTM da URL da landing page. +### Como testar: +Na tela de chat, conferir se os Atributos `lp`, `campanha`, `origem` e `click_id` estão sendo preenchidos para novos contatos do WhatsApp criados através de LPs. + +## Parte 2 - Multiunidade (Server-side LPs) +- **Problema:** As Landing Pages dependiam do `localStorage` do navegador para manter configurações (nº de telefone, titulo, etc). Isso tornava impossível rastreamento persistente (quando o usuário trocava de celular) e não suportava estrutura multiunidade profissional. +- **Implementação:** + 1. Feita migração adicionando à tabela `landing_hosts` os campos faltantes: `initial_message`, `default_source`, `default_campanha`, `custom_config`. + 2. Atualizamos `LandingPagesController` (Controller Backend) para carregar os dados baseando-se no `request.host`. + 3. Modificamos o HTML de exibição principal (`app/views/public/landing_pages/show.html.erb`), removendo painel de administração overlay e substituindo por ERB para renderização Server-Side (SSG). + +## Em Andamento / Concluídos Recentes +1. **Backend**: Atualizar o `TrackingController` para salvar `click_id` e extrair UTMs caso não sejam enviados explicitamente. +2. Validar se o `click_id` está sendo recebido no Controller corretamente (testado via postman/local e aprovado - os testes do Sidekiq estão rodando no log). +3. Criar painel no Dashboard para gerenciar `landing_hosts` (migrando configurações do front-end armazenadas no LocalStorage para Back-end em estrutura multi-unidade) -> **Concluído:** Criada interface em Inboxes > Configurações > Landing Pages. + ### Próximos Passos 1. **Migração**: Adicionar o campo `click_id` na tabela `lead_clicks`. -2. **Backend**: Atualizar o `TrackingController` para salvar `click_id` e extrair UTMs caso não sejam enviados explicitamente. 3. **Frontend**: Sugerir script JS para a landing page que capture UTMs da URL e as envie para o Supabase. +- Adicionar interface "Painel Admin" para Gerenciar Landing Pages dentro das configurações originais do Chatwoot (Vue Frontend). +- Conferir os Logs de tracking e Sidekiq em mensagens locais para debugar qualquer problema residual no `AttributionMatcherService`. ### Como Validar Simular um clique com UTMs: diff --git a/progresso/landing_page_dinamica.md b/progresso/landing_page_dinamica.md new file mode 100644 index 000000000..e69de29bb diff --git a/test_landing_page.sh b/test_landing_page.sh new file mode 100755 index 000000000..4d304b2ea --- /dev/null +++ b/test_landing_page.sh @@ -0,0 +1,20 @@ +#!/bin/bash +# Script de teste rápido da Landing Page simulando tráfego + +# Defina a URL base local, se o porto for 3000 +BASE_URL="http://localhost:3000/lp" + +# Imprime o título de teste e a URL para clicar +echo "---------------------------------------------------------" +echo "Teste a sua Landing Page abrindo este link no navegador:" +echo "" +echo " 👉 $BASE_URL" +echo "" +echo "Teste também com UTMs simulando um clique de Anúncio:" +echo "" +echo " 👉 $BASE_URL?utm_source=meta&utm_medium=cpc&utm_campaign=black_friday" +echo "" +echo "Abra os links acima. Eles buscarão as configurações salvas" +echo "pelo seu painel do Chatwoot (se houver configuração" +echo "para 'localhost', ela aparecerá)." +echo "---------------------------------------------------------"