diff --git a/src/pages/api/rsvp.ts b/src/pages/api/rsvp.ts index 6206dd6..ae4c0f1 100644 --- a/src/pages/api/rsvp.ts +++ b/src/pages/api/rsvp.ts @@ -9,16 +9,19 @@ const objectsToCSV = (data: RSVPItem[]): string => { data.forEach((entry) => { const row = headers.map((header) => { - let field = entry[header as keyof RSVPItem]; - // Convert boolean to string for CSV - if (typeof field === 'boolean') { - field = field.toString(); + let value = entry[header as keyof RSVPItem]; + + // Ensure proper value formatting + if (header === "attending") { + value = Boolean(value).toString(); + } else if (value === null || value === undefined) { + value = ""; } - // Handle null/undefined - if (field === null || field === undefined) { - field = ''; - } - const escaped = String(field).replace(/"/g, '""'); + + // Escape special characters + const escaped = String(value) + .replace(/"/g, '""') + .replace(/\n/g, ' '); return `"${escaped}"`; }); csvRows.push(row.join(",")); @@ -30,30 +33,59 @@ const objectsToCSV = (data: RSVPItem[]): string => { const csvToObjects = (csv: string): RSVPItem[] => { const lines = csv.split("\n"); const headers = lines[0].split(",").map((h) => h.trim()); - const hasAttendingColumn = headers.includes("attending"); return lines .slice(1) .filter((line) => line.trim()) .map((line) => { - const values = line.match(/(".*?"|[^",\s]+)(?=\s*,|\s*$)/g) || []; - const entry: Partial = {}; + // More robust CSV parsing + const values: string[] = []; + let inQuote = false; + let currentValue = ''; + + for (let i = 0; i < line.length; i++) { + const char = line[i]; + + if (char === '"') { + if (inQuote && line[i + 1] === '"') { + // Handle escaped quotes + currentValue += '"'; + i++; + } else { + inQuote = !inQuote; + } + } else if (char === ',' && !inQuote) { + values.push(currentValue.trim()); + currentValue = ''; + } else { + currentValue += char; + } + } + values.push(currentValue.trim()); + // Create the entry object + const entry: Partial = {}; headers.forEach((header, index) => { let value = values[index] || ""; value = value.replace(/^"(.*)"$/, "$1").replace(/""/g, '"'); - entry[header as keyof RSVPItem] = value; + + if (header === "attending") { + // Explicitly convert to boolean + entry.attending = value.toLowerCase() === "true"; + } else { + entry[header as keyof RSVPItem] = value; + } }); - // If the attending column doesn't exist in the CSV, assume they're attending if they provided dietary restrictions - if (!hasAttendingColumn) { - entry.attending = Boolean(entry.dietaryRestrictions); - } else if (typeof entry.attending === 'string') { - entry.attending = entry.attending.toLowerCase() === 'true'; - } - return entry as RSVPItem; - }); + }) + .filter(entry => + // Filter out malformed entries + entry.name && + typeof entry.attending === 'boolean' && + typeof entry.timestamp === 'string' && + entry.timestamp.match(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/) + ); }; // GET: Retrieve all RSVPs (requires admin role)