diff --git a/apps/ops/src/modules/campaigns/pages/CampaignNewPage.vue b/apps/ops/src/modules/campaigns/pages/CampaignNewPage.vue index 741f0b2..22e241d 100644 --- a/apps/ops/src/modules/campaigns/pages/CampaignNewPage.vue +++ b/apps/ops/src/modules/campaigns/pages/CampaignNewPage.vue @@ -22,7 +22,7 @@
- ✓ {{ Math.max(0, (mapPreview.match(/\n/g)||[]).length - 1) }} lignes lues + ✓ {{ countMapRows(mapPreview) }} lignes de contacts (préambule + header retirés)
@@ -40,7 +40,8 @@
- ✓ {{ Math.max(0, (giftPreview.match(/\n/g)||[]).length - 1) }} cartes-cadeaux + ✓ {{ countGiftRows(giftPreview) }} cartes-cadeaux + (format: {{ giftFormatHint }})
@@ -234,6 +235,44 @@ function readFile (file) { async function readMapFile (file) { if (file) mapPreview.value = await readFile(file) } async function readGiftFile (file) { if (file) giftPreview.value = await readFile(file) } +// Counts must mirror the backend parser exactly so the user sees the same +// numbers in the preview as what Step 2 will receive. + +// Map CSV format: 1-line title preamble + header row + N data rows. +// Returns N (the # of contact lines, excluding the preamble and header). +function countMapRows (text) { + if (!text) return 0 + const lines = text.split(/\r?\n/).filter(l => l.trim()) + // -1 for preamble, -1 for header + return Math.max(0, lines.length - 2) +} + +// Giftbit CSV: TWO formats +// 1. "Link Order" — headerless, one URL per line (each URL = 1 gift) +// 2. "Campaign export" — header row + N data rows (-1 for header) +// Detect like the backend: first non-empty line is a bare URL with no +// separator → no header. +function countGiftRows (text) { + if (!text) return 0 + const cleaned = text.replace(/^/, '').trim() + if (!cleaned) return 0 + const firstLine = cleaned.split(/\r?\n/, 1)[0].trim() + const isLinkOrder = /^https?:\/\/\S+$/.test(firstLine) && + !firstLine.includes(',') && !firstLine.includes('\t') && !firstLine.includes('|') + const lines = cleaned.split(/\r?\n/).filter(l => l.trim()) + return isLinkOrder ? lines.length : Math.max(0, lines.length - 1) +} + +// Show "Link Order" or "Campaign export" hint next to the gift count +const giftFormatHint = computed(() => { + if (!giftPreview.value) return '' + const firstLine = giftPreview.value.replace(/^/, '').trim().split(/\r?\n/, 1)[0].trim() + if (/^https?:\/\/\S+$/.test(firstLine) && !firstLine.includes(',') && !firstLine.includes('\t') && !firstLine.includes('|')) { + return 'Link Order' + } + return 'Campaign export' +}) + async function goPreview () { if (!mapPreview.value || !giftPreview.value) return parsing.value = true