Aggiunge IMAP bounce e flag email non valide nel CRM.
CI / scan_ruby (push) Failing after 13m27s
CI / scan_js (push) Successful in 11m45s
CI / lint (push) Failing after 12m4s

Permette di leggere i bounce dalla stessa MailIdentity SMTP, aggiornare le email in anagrafica e saltare gli indirizzi invalidi nei mailing.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-08 19:46:04 +02:00
co-authored by Cursor
parent 353ace4d99
commit 36b3ffe507
26 changed files with 922 additions and 12 deletions
+8
View File
@@ -23,6 +23,14 @@
<label class="block text-sm font-medium">Email</label>
<%= f.email_field :email, placeholder: "Email", class: input_class %>
</div>
<div class="space-y-1">
<label class="block text-sm font-medium">Email bounce</label>
<%= f.email_field :bounced_email, placeholder: "Indirizzo non recapitabile", class: input_class %>
</div>
<label class="flex min-h-11 items-center gap-3 rounded-lg border border-zinc-200 px-3 py-2 text-sm dark:border-zinc-700 sm:col-span-2">
<%= f.check_box :email_invalid, class: "size-5" %>
Email non valida / bounce
</label>
<div class="space-y-1">
<label class="block text-sm font-medium">Telefono</label>
<%= f.text_field :phone, placeholder: "Telefono", class: input_class %>
+17
View File
@@ -42,7 +42,24 @@
<div>
<label class="mb-1 block text-sm font-medium">Password SMTP<%= " (lascia vuoto per non cambiare)" unless mail_identity.new_record? %></label>
<%= f.password_field :smtp_password, autocomplete: "new-password", class: input_class %>
<p class="mt-1 text-xs text-zinc-500">Stessa password usata anche per IMAP (lettura bounce).</p>
</div>
<div class="md:col-span-2 border-t border-zinc-100 pt-4 dark:border-zinc-800">
<h2 class="text-sm font-semibold">IMAP (lettura casella / bounce)</h2>
<p class="mt-1 text-xs text-zinc-500">Di solito host e porta si compilano da soli in base allSMTP (es. Aruba → imaps.aruba.it:993).</p>
</div>
<div>
<label class="mb-1 block text-sm font-medium">Host IMAP</label>
<%= f.text_field :imap_host, placeholder: mail_identity.inferred_imap_host || "imaps.aruba.it", class: input_class %>
</div>
<div>
<label class="mb-1 block text-sm font-medium">Porta IMAP</label>
<%= f.number_field :imap_port, min: 1, max: 65535, class: input_class %>
</div>
<label class="flex min-h-11 items-center gap-3 rounded-lg border border-zinc-200 px-3 py-2 text-sm dark:border-zinc-700 md:col-span-2">
<%= f.check_box :imap_enabled, class: "size-5" %>
Abilita lettura IMAP (bounce) con le stesse credenziali SMTP
</label>
<label class="flex min-h-11 items-center gap-3 rounded-lg border border-zinc-200 px-3 py-2 text-sm dark:border-zinc-700">
<%= f.check_box :verify_ssl, class: "size-5" %>
Verifica il certificato SSL del server
+18 -2
View File
@@ -1,10 +1,10 @@
<div class="space-y-6">
<div class="flex flex-wrap items-end justify-between gap-3">
<div>
<h1 class="text-xl font-semibold tracking-tight md:text-2xl">Account email / SMTP</h1>
<h1 class="text-xl font-semibold tracking-tight md:text-2xl">Account email / SMTP + IMAP</h1>
<p class="mt-1 text-sm text-zinc-500">
<%= link_to "Impostazioni", admin_path, class: "hover:underline" %>
· da questi indirizzi partono le campagne. Le password SMTP sono cifrate.
· SMTP per gli invii, IMAP (stesse credenziali) per leggere i bounce. Password cifrate.
</p>
</div>
<%= link_to "Nuovo account", new_mail_identity_path, class: "#{btn_primary} w-full sm:w-auto" %>
@@ -26,6 +26,14 @@
<%= link_to "Modifica", edit_mail_identity_path(identity), class: "text-sm text-zinc-700 hover:underline dark:text-zinc-300" %>
</div>
<div class="mt-2 text-sm text-zinc-600 dark:text-zinc-300"><%= identity.smtp_host %>:<%= identity.smtp_port %> · <%= Catalog.label_for(Catalog::MAIL_ENCRYPTIONS, identity.encryption) %></div>
<div class="mt-1 text-sm text-zinc-600 dark:text-zinc-300">
IMAP:
<% if identity.imap_ready? %>
<%= identity.effective_imap_host %>:<%= identity.effective_imap_port %>
<% else %>
off
<% end %>
</div>
<div class="mt-1 text-sm text-zinc-500"><%= identity.active? ? "Attivo" : "Disattivo" %></div>
</div>
<% end %>
@@ -38,6 +46,7 @@
<th class="px-4 py-3">Nome</th>
<th class="px-4 py-3">Mittente</th>
<th class="px-4 py-3">SMTP</th>
<th class="px-4 py-3">IMAP</th>
<th class="px-4 py-3">Stato</th>
<th class="px-4 py-3"></th>
</tr>
@@ -54,6 +63,13 @@
<%= identity.smtp_host %>:<%= identity.smtp_port %>
<div class="text-xs text-zinc-500"><%= Catalog.label_for(Catalog::MAIL_ENCRYPTIONS, identity.encryption) %></div>
</td>
<td class="px-4 py-3 text-zinc-600 dark:text-zinc-300">
<% if identity.imap_ready? %>
<%= identity.effective_imap_host %>:<%= identity.effective_imap_port %>
<% else %>
<span class="text-zinc-400">off</span>
<% end %>
</td>
<td class="px-4 py-3"><%= identity.active? ? "Attivo" : "Disattivo" %></td>
<td class="px-4 py-3 text-right">
<%= link_to "Modifica", edit_mail_identity_path(identity), class: "text-zinc-700 hover:underline dark:text-zinc-300" %>
+7
View File
@@ -34,6 +34,13 @@
<div class="sm:col-span-2"><%= f.label :source_url, class: "mb-1 block text-sm font-medium" %><%= f.text_field :source_url, class: input_class %></div>
<div><%= f.label :phone, class: "mb-1 block text-sm font-medium" %><%= f.text_field :phone, class: input_class %></div>
<div><%= f.label :email, class: "mb-1 block text-sm font-medium" %><%= f.email_field :email, class: input_class %></div>
<div class="flex items-end gap-2 pb-1">
<label class="inline-flex items-center gap-2 text-sm">
<%= f.check_box :email_invalid, class: "rounded border-zinc-300" %>
Email non valida / bounce
</label>
</div>
<div><%= f.label :bounced_email, "Email bounce", class: "mb-1 block text-sm font-medium" %><%= f.email_field :bounced_email, class: input_class %></div>
<div><%= f.label :vat_number, class: "mb-1 block text-sm font-medium" %><%= f.text_field :vat_number, class: input_class %></div>
<div><%= f.label :verified_at, class: "mb-1 block text-sm font-medium" %><%= f.date_field :verified_at, class: input_class %></div>
<div class="sm:col-span-2"><%= f.label :commercial_fit, class: "mb-1 block text-sm font-medium" %><%= f.text_area :commercial_fit, rows: 3, class: input_class %></div>
+18 -2
View File
@@ -74,7 +74,18 @@
<div><dt class="text-slate-500">Sport</dt><dd class="font-medium"><%= [@organization.sport.presence, @organization.team_gender_label.presence].compact.join(" · ").presence || "—" %></dd></div>
<div><dt class="text-slate-500">Località</dt><dd class="font-medium"><%= [@organization.city, @organization.province, @organization.region, @organization.country].compact_blank.join(", ").presence || "—" %></dd></div>
<div><dt class="text-slate-500">Lista campagna</dt><dd class="font-medium"><%= @organization.list_position || "—" %></dd></div>
<div><dt class="text-slate-500">Email</dt><dd class="font-medium break-anywhere"><%= mailto_link(@organization.email, class: "text-sky-700 hover:underline dark:text-sky-400") %></dd></div>
<div>
<dt class="text-slate-500">Email</dt>
<dd class="font-medium break-anywhere">
<%= mailto_link(@organization.email, class: "text-sky-700 hover:underline dark:text-sky-400") %>
<% if @organization.email_invalid? %>
<span class="ml-1 rounded bg-rose-100 px-1.5 py-0.5 text-[10px] font-semibold uppercase text-rose-800 dark:bg-rose-950 dark:text-rose-200">Non valida</span>
<% end %>
<% if @organization.bounced_email.present? && @organization.bounced_email != @organization.email %>
<div class="mt-1 text-xs text-rose-600 dark:text-rose-300">Bounce: <%= @organization.bounced_email %></div>
<% end %>
</dd>
</div>
<div><dt class="text-slate-500">Telefono</dt><dd class="font-medium"><%= tel_link(@organization.phone, class: "text-sky-700 hover:underline dark:text-sky-400") %></dd></div>
<div><dt class="text-slate-500">Sito / profilo</dt><dd class="font-medium"><%= external_link(@organization.website, class: "text-sky-700 hover:underline dark:text-sky-400") %></dd></div>
<div><dt class="text-slate-500">Fonte ricerca</dt><dd class="font-medium"><%= external_link(@organization.source_url, class: "text-sky-700 hover:underline dark:text-sky-400") %></dd></div>
@@ -98,7 +109,12 @@
<%= contact.full_name %>
<% if contact.primary_contact? %><span class="ml-1 rounded bg-emerald-100 px-1.5 py-0.5 text-[10px] font-semibold uppercase text-emerald-800 dark:bg-emerald-950 dark:text-emerald-200">Principale</span><% end %>
</div>
<div class="break-anywhere text-sm text-slate-500"><%= [contact.role, contact.email, contact.phone.presence || contact.mobile].compact_blank.join(" · ") %></div>
<div class="break-anywhere text-sm text-slate-500">
<%= [contact.role, contact.email, contact.phone.presence || contact.mobile].compact_blank.join(" · ") %>
<% if contact.email_invalid? %>
<span class="ml-1 rounded bg-rose-100 px-1.5 py-0.5 text-[10px] font-semibold uppercase text-rose-800 dark:bg-rose-950 dark:text-rose-200">Email non valida</span>
<% end %>
</div>
</div>
<%= link_to "Modifica", edit_contact_path(contact), class: "text-sm text-zinc-600 hover:underline dark:text-zinc-300" %>
</div>