« Total mensuel » = somme des abonnements actifs du client (0 = aucun filtre). « Résidentiel/Commercial » = type de compte.
+ Le « total mensuel » (somme des abonnements actifs) requiert d'activer « Abonnement actif ». « Résidentiel/Commercial » = type de compte.
@@ -163,6 +163,18 @@
ex. {{ audPreview.sample.slice(0, 4).map(s => s.firstname || s.email).join(', ') }}…
+
@@ -220,6 +232,7 @@ const csvFile = ref(null)
const audCsvText = ref('')
const audPreview = ref(null)
const previewing = ref(false)
+const showNoEmail = ref(false)
const publicUrl = computed(() => ev.value.public_url || '')
@@ -301,8 +314,9 @@ function onCsvFile (file) {
rd.onload = () => { audCsvText.value = String(rd.result || '') }
rd.readAsText(file)
}
+function onActiveSubToggle (v) { audPreview.value = null; if (!v) audFilters.value.min_monthly = 0 } // le montant n'a de sens qu'avec les abonnements actifs
async function doPreview () {
- previewing.value = true; audPreview.value = null
+ previewing.value = true; audPreview.value = null; showNoEmail.value = false
try {
const body = audienceSource.value === 'csv' ? { mode: 'csv', csv: audCsvText.value } : { mode: 'filter', filters: audFilters.value }
audPreview.value = await previewAudience(EVENT_ID, body)
diff --git a/services/targo-hub/lib/events.js b/services/targo-hub/lib/events.js
index 491c0ca..48e35ed 100644
--- a/services/targo-hub/lib/events.js
+++ b/services/targo-hub/lib/events.js
@@ -294,13 +294,17 @@ async function resolveAudience ({ mode = 'filter', filters = {}, csv = '' } = {}
}
rows = customers.map(c => { const n = splitName(c.customer_name); return { email: firstEmail(c.email_id), firstname: n.firstname, lastname: n.lastname, language: normLang(c.language), customer_id: c.name, customer_type: c.customer_type || '', monthly: Math.round(c._monthly || 0) } })
}
- const seen = new Set(); const recipients = []; let dropped = 0
+ const seen = new Set(); const recipients = []; const noEmail = []; let dropped = 0
for (const r of rows) {
- if (!EMAIL_RE.test(r.email || '')) { dropped++; continue }
+ if (!EMAIL_RE.test(r.email || '')) {
+ dropped++
+ if (r.customer_id) noEmail.push({ customer_id: r.customer_id, name: ((r.firstname || '') + ' ' + (r.lastname || '')).trim() || r.customer_id }) // rattachable → lien fiche pour valider
+ continue
+ }
const k = r.email.toLowerCase(); if (seen.has(k)) continue; seen.add(k)
recipients.push(r)
}
- return { recipients, count: recipients.length, dropped_no_email: dropped }
+ return { recipients, count: recipients.length, dropped_no_email: dropped, no_email: noEmail }
}
// ── Page publique (festive, mobile-first, bilingue) ────────────────────────
@@ -565,7 +569,7 @@ async function handle (req, res, method, path, url) {
const b = await parseBody(req)
const r = await resolveAudience({ mode: b.mode, filters: b.filters || {}, csv: b.csv || '' })
if (r.error) return json(res, 400, { error: r.error })
- return json(res, 200, { count: r.count, dropped_no_email: r.dropped_no_email, sample: r.recipients.slice(0, 50) })
+ return json(res, 200, { count: r.count, dropped_no_email: r.dropped_no_email, sample: r.recipients.slice(0, 50), no_email: (r.no_email || []).slice(0, 300) })
}
return json(res, 404, { error: 'not found' })