diff --git a/apps/ops/src/modules/campaigns/pages/CampaignNewPage.vue b/apps/ops/src/modules/campaigns/pages/CampaignNewPage.vue
index df61092..c87d279 100644
--- a/apps/ops/src/modules/campaigns/pages/CampaignNewPage.vue
+++ b/apps/ops/src/modules/campaigns/pages/CampaignNewPage.vue
@@ -150,6 +150,21 @@
+
+
+
+
+ Cliquer pour basculer FR ↔ EN
+
+
+
@@ -212,6 +227,7 @@
Nom{{ params.name }}
Destinataires actifs{{ sendableCount }} (sur {{ recipients.length }} paires ; {{ excludedCount }} exclus, {{ unpairedContacts.length }} sans lien)
+ Répartition par langue{{ langBreakdown }}
Montant affiché{{ params.amount }}
Engagement{{ params.commitment_months }} mois
Sujet{{ params.subject }}
@@ -285,7 +301,8 @@ const recipientColumns = [
{ name: 'email', label: 'Email', field: 'email', align: 'left' },
{ name: 'civic_address', label: 'Adresse', field: 'civic_address', align: 'left' },
{ name: 'gift_url', label: 'Lien-cadeau', field: 'gift_url', align: 'left' },
- { name: 'match', label: 'Match client', field: 'match_method', align: 'left' },
+ { name: 'language', label: 'Langue', field: 'language', align: 'left' },
+ { name: 'match', label: 'Match', field: 'match_method', align: 'left' },
{ name: 'customer_name', label: 'Client ERPNext', field: 'customer_name', align: 'left' },
{ name: 'actions', label: '', field: '', align: 'right' },
]
@@ -304,6 +321,19 @@ const unmatchedCount = computed(() => recipients.value.filter(r => !r.customer_i
const excludedCount = computed(() => recipients.value.filter(r => r.excluded).length)
// Net number of emails that will actually be fired off (paired AND not excluded)
const sendableCount = computed(() => recipients.value.filter(r => !r.excluded && r.gift_url).length)
+
+// FR / EN breakdown of the sendable recipients — useful preview before launch
+// so the user knows which template will actually be used and how many.
+const langBreakdown = computed(() => {
+ const counts = {}
+ for (const r of recipients.value) {
+ if (r.excluded || !r.gift_url) continue
+ const lang = (r.language || 'fr').toLowerCase()
+ counts[lang] = (counts[lang] || 0) + 1
+ }
+ const parts = Object.entries(counts).map(([l, n]) => `${n} × ${l.toUpperCase()}`)
+ return parts.length ? parts.join(', ') : '—'
+})
const estimatedMinutes = computed(() => {
const per = (params.value.throttle_ms || 600) / 1000
return Math.max(1, Math.round((sendableCount.value * per) / 60))
diff --git a/apps/ops/src/modules/campaigns/pages/TemplateEditorPage.vue b/apps/ops/src/modules/campaigns/pages/TemplateEditorPage.vue
index 7c09f48..6da30d2 100644
--- a/apps/ops/src/modules/campaigns/pages/TemplateEditorPage.vue
+++ b/apps/ops/src/modules/campaigns/pages/TemplateEditorPage.vue
@@ -50,7 +50,13 @@
Blocs conditionnels : {{#expiry}}...{{/expiry}}.
+
@@ -62,7 +68,7 @@