Carica oggetto e corpo dell'email appena si sceglie un template A/B.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -166,19 +166,18 @@ class MailingsController < ApplicationController
|
||||
end
|
||||
|
||||
def apply_template_if_needed
|
||||
copy_template(params.dig(:mailing, :mail_template_id), :subject, :body_html, params[:use_template_content])
|
||||
copy_template(params.dig(:mailing, :mail_template_b_id), :subject_b, :body_html_b, params[:use_template_b_content])
|
||||
copy_template_if_blank(params.dig(:mailing, :mail_template_id), :subject, :body_html)
|
||||
copy_template_if_blank(params.dig(:mailing, :mail_template_b_id), :subject_b, :body_html_b)
|
||||
end
|
||||
|
||||
def copy_template(template_id, subject_attr, body_attr, flag)
|
||||
def copy_template_if_blank(template_id, subject_attr, body_attr)
|
||||
return if template_id.blank?
|
||||
return unless ActiveModel::Type::Boolean.new.cast(flag)
|
||||
|
||||
template = MailTemplate.for_project(current_project).find_by(id: template_id)
|
||||
return unless template
|
||||
|
||||
@mailing.public_send("#{subject_attr}=", template.subject)
|
||||
@mailing.public_send("#{body_attr}=", template.body_html)
|
||||
@mailing.public_send("#{subject_attr}=", template.subject) if @mailing.public_send(subject_attr).blank?
|
||||
@mailing.public_send("#{body_attr}=", template.body_html) if @mailing.public_send(body_attr).blank?
|
||||
end
|
||||
|
||||
def rebuild_recipients_if_needed
|
||||
|
||||
@@ -21,6 +21,10 @@ module ApplicationHelper
|
||||
"w-full rounded-lg border border-zinc-200 bg-white px-3 py-2 text-sm text-zinc-900 placeholder:text-zinc-400 focus:border-zinc-400 focus:outline-none focus:ring-1 focus:ring-zinc-400 dark:border-zinc-700 dark:bg-zinc-950 dark:text-zinc-100 dark:placeholder:text-zinc-500 dark:focus:border-zinc-500 dark:focus:ring-zinc-500"
|
||||
end
|
||||
|
||||
def mail_templates_picker_payload(templates)
|
||||
Array(templates).map { |template| { id: template.id, subject: template.subject, body_html: template.body_html } }
|
||||
end
|
||||
|
||||
def card_class
|
||||
"rounded-xl border border-zinc-200 bg-white text-zinc-900 dark:border-zinc-800 dark:bg-zinc-900 dark:text-zinc-100"
|
||||
end
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
import { Controller } from "@hotwired/stimulus"
|
||||
|
||||
export default class extends Controller {
|
||||
static targets = ["select", "subject"]
|
||||
static values = { templates: Array }
|
||||
|
||||
connect() {
|
||||
this.pendingHtml = null
|
||||
this.onEditorReady = this.onEditorReady.bind(this)
|
||||
this.editorElement?.addEventListener("trix-initialize", this.onEditorReady)
|
||||
if (this.shouldPrefill()) this.apply()
|
||||
}
|
||||
|
||||
disconnect() {
|
||||
this.editorElement?.removeEventListener("trix-initialize", this.onEditorReady)
|
||||
}
|
||||
|
||||
apply() {
|
||||
const template = this.currentTemplate()
|
||||
if (!template) return
|
||||
|
||||
if (this.hasSubjectTarget) {
|
||||
this.subjectTarget.value = template.subject || ""
|
||||
this.subjectTarget.dispatchEvent(new Event("input", { bubbles: true }))
|
||||
}
|
||||
|
||||
this.setBody(template.body_html || "")
|
||||
}
|
||||
|
||||
shouldPrefill() {
|
||||
if (!this.selectTarget.value) return false
|
||||
const subjectBlank = !this.hasSubjectTarget || this.subjectTarget.value.trim() === ""
|
||||
return subjectBlank && this.bodyIsBlank()
|
||||
}
|
||||
|
||||
currentTemplate() {
|
||||
const id = Number(this.selectTarget.value)
|
||||
if (!id) return null
|
||||
return (this.templatesValue || []).find((template) => Number(template.id) === id) || null
|
||||
}
|
||||
|
||||
setBody(html) {
|
||||
this.pendingHtml = html
|
||||
const editor = this.editorElement?.editor
|
||||
if (editor) {
|
||||
editor.loadHTML(html)
|
||||
this.pendingHtml = null
|
||||
return
|
||||
}
|
||||
if (this.hiddenInput) this.hiddenInput.value = html
|
||||
}
|
||||
|
||||
onEditorReady() {
|
||||
if (this.pendingHtml != null) {
|
||||
this.editorElement.editor?.loadHTML(this.pendingHtml)
|
||||
this.pendingHtml = null
|
||||
return
|
||||
}
|
||||
if (this.shouldPrefill()) this.apply()
|
||||
}
|
||||
|
||||
bodyIsBlank() {
|
||||
const value = this.hiddenInput?.value || ""
|
||||
return value.replace(/<[^>]*>/g, "").replace(/ /g, " ").trim() === ""
|
||||
}
|
||||
|
||||
get editorElement() {
|
||||
return this.element.querySelector("trix-editor")
|
||||
}
|
||||
|
||||
get hiddenInput() {
|
||||
return this.element.querySelector("[data-wysiwyg-target='input']")
|
||||
}
|
||||
}
|
||||
@@ -37,7 +37,7 @@
|
||||
<%= f.check_box :ab_test, { data: { ab_test_target: "checkbox", action: "ab-test#toggle" } } %>
|
||||
<span>
|
||||
<span class="font-medium">Test A/B su questa comunicazione</span>
|
||||
<span class="mt-1 block text-xs text-zinc-500">Due versioni di oggetto e HTML. Ogni destinatario riceve A o B.</span>
|
||||
<span class="mt-1 block text-xs text-zinc-500">Due versioni della stessa email. Carica un template A e un template B nelle colonne: ogni destinatario ne riceve una.</span>
|
||||
</span>
|
||||
</label>
|
||||
|
||||
@@ -48,48 +48,43 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<% templates_payload = mail_templates_picker_payload(@mail_templates) %>
|
||||
<div class="grid gap-6 <%= "lg:grid-cols-2" if mailing.ab_test? %>" data-ab-test-target="grid">
|
||||
<div class="space-y-4">
|
||||
<%= tag.div class: "space-y-4", data: { controller: "template-picker", template_picker_templates_value: templates_payload } do %>
|
||||
<h2 class="text-sm font-semibold uppercase tracking-wide text-zinc-500">Variante A</h2>
|
||||
<div>
|
||||
<label class="mb-1 block text-sm font-medium">Template A (opzionale)</label>
|
||||
<%= f.collection_select :mail_template_id, @mail_templates, :id, :name, { include_blank: "Nessuno" }, { class: input_class } %>
|
||||
<label class="mb-1 block text-sm font-medium">Template A</label>
|
||||
<%= f.collection_select :mail_template_id, @mail_templates, :id, :name,
|
||||
{ include_blank: "Nessuno — scrivi a mano" },
|
||||
{ class: input_class, data: { template_picker_target: "select", action: "change->template-picker#apply" } } %>
|
||||
<p class="mt-1 text-xs text-zinc-500">Scegliendo un template, oggetto e testo si copiano qui sotto. Poi puoi modificarli solo per questo invio.</p>
|
||||
</div>
|
||||
<% unless mailing.new_record? %>
|
||||
<label class="flex items-center gap-2 text-sm">
|
||||
<%= check_box_tag :use_template_content, "1", false %>
|
||||
Sostituisci oggetto e HTML A dal template
|
||||
</label>
|
||||
<% end %>
|
||||
<div>
|
||||
<label class="mb-1 block text-sm font-medium">Oggetto A</label>
|
||||
<%= f.text_field :subject, required: true, class: input_class %>
|
||||
<%= f.text_field :subject, required: true, class: input_class, data: { template_picker_target: "subject" } %>
|
||||
</div>
|
||||
<div>
|
||||
<%= render "shared/wysiwyg_field", form: f, method: :body_html, label: "Corpo HTML A" %>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<div class="space-y-4 <%= "hidden" unless mailing.ab_test? %>" data-ab-test-target="panel">
|
||||
<%= tag.div class: "space-y-4 #{'hidden' unless mailing.ab_test?}", data: { controller: "template-picker", template_picker_templates_value: templates_payload, ab_test_target: "panel" } do %>
|
||||
<h2 class="text-sm font-semibold uppercase tracking-wide text-zinc-500">Variante B</h2>
|
||||
<div>
|
||||
<label class="mb-1 block text-sm font-medium">Template B (opzionale)</label>
|
||||
<%= f.collection_select :mail_template_b_id, @mail_templates, :id, :name, { include_blank: "Nessuno" }, { class: input_class } %>
|
||||
<label class="mb-1 block text-sm font-medium">Template B</label>
|
||||
<%= f.collection_select :mail_template_b_id, @mail_templates, :id, :name,
|
||||
{ include_blank: "Nessuno — scrivi a mano" },
|
||||
{ class: input_class, data: { template_picker_target: "select", action: "change->template-picker#apply" } } %>
|
||||
<p class="mt-1 text-xs text-zinc-500">Stesso meccanismo della variante A: il template riempie oggetto e corpo B.</p>
|
||||
</div>
|
||||
<% unless mailing.new_record? %>
|
||||
<label class="flex items-center gap-2 text-sm">
|
||||
<%= check_box_tag :use_template_b_content, "1", false %>
|
||||
Sostituisci oggetto e HTML B dal template
|
||||
</label>
|
||||
<% end %>
|
||||
<div>
|
||||
<label class="mb-1 block text-sm font-medium">Oggetto B</label>
|
||||
<%= f.text_field :subject_b, class: input_class %>
|
||||
<%= f.text_field :subject_b, class: input_class, data: { template_picker_target: "subject" } %>
|
||||
</div>
|
||||
<div>
|
||||
<%= render "shared/wysiwyg_field", form: f, method: :body_html_b, label: "Corpo HTML B" %>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
Reference in New Issue
Block a user