fix(ops/campaigns): correct row counts in Step 1 — Link Order CSV had no header
The Step 1 file-upload widgets displayed `(newlines) - 1` for both CSVs, assuming both files have a header row to discount. This breaks for the Giftbit Link Order export which is headerless (one URL per line): a 3-URL file was showing "2 cartes-cadeaux" because the parser ate URL #1 as a fake header. The backend parser was already correct (detects Link Order vs Campaign format by inspecting the first line). The bug was UI-only — the count display reused the same arithmetic for both formats. Fix: introduce countMapRows / countGiftRows helpers that mirror the backend's format detection. Map CSV subtracts 2 (preamble + header). Gift CSV subtracts 0 for Link Order (headerless) or 1 for Campaign export (with header). Plus a "(format: Link Order)" hint next to the count so the user sees which detection path was taken. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
ff629a6a85
commit
0186a7318e
|
|
@ -22,7 +22,7 @@
|
|||
<template v-slot:prepend><q-icon name="attach_file" /></template>
|
||||
</q-file>
|
||||
<div v-if="mapPreview" class="text-caption q-mt-sm text-grey-7">
|
||||
✓ {{ Math.max(0, (mapPreview.match(/\n/g)||[]).length - 1) }} lignes lues
|
||||
✓ {{ countMapRows(mapPreview) }} lignes de contacts (préambule + header retirés)
|
||||
</div>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
|
|
@ -40,7 +40,8 @@
|
|||
<template v-slot:prepend><q-icon name="attach_file" /></template>
|
||||
</q-file>
|
||||
<div v-if="giftPreview" class="text-caption q-mt-sm text-grey-7">
|
||||
✓ {{ Math.max(0, (giftPreview.match(/\n/g)||[]).length - 1) }} cartes-cadeaux
|
||||
✓ {{ countGiftRows(giftPreview) }} cartes-cadeaux
|
||||
<span v-if="giftFormatHint" class="text-grey-6">(format: {{ giftFormatHint }})</span>
|
||||
</div>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user