feat(admin): improve export to put guests in their own row

This commit is contained in:
2026-03-19 10:31:12 +01:00
parent ee0e9e68a9
commit 845624dfd3

View File

@@ -663,76 +663,24 @@ export const appRouter = {
const escapeCell = (val: string) => `"${val.replace(/"/g, '""')}"`;
// Main registration headers
// Each person (registrant + guests) gets their own row.
// Guest rows reference the registrant via "Registration ID".
const headers = [
"ID",
"Row Type", // "registrant" | "guest"
"Registration ID", // UUID of the parent registration (same for registrant + its guests)
"First Name",
"Last Name",
"Email",
"Phone",
"Birthdate",
"Postcode",
// Registrant-only fields (blank for guest rows)
"Type",
"Art Form",
"Experience",
"Is Over 16",
"Drink Card Value (EUR)",
"Gift Amount (cents)",
"Guest Count",
"Guest 1 First Name",
"Guest 1 Last Name",
"Guest 1 Email",
"Guest 1 Phone",
"Guest 1 Birthdate",
"Guest 1 Postcode",
"Guest 2 First Name",
"Guest 2 Last Name",
"Guest 2 Email",
"Guest 2 Phone",
"Guest 2 Birthdate",
"Guest 2 Postcode",
"Guest 3 First Name",
"Guest 3 Last Name",
"Guest 3 Email",
"Guest 3 Phone",
"Guest 3 Birthdate",
"Guest 3 Postcode",
"Guest 4 First Name",
"Guest 4 Last Name",
"Guest 4 Email",
"Guest 4 Phone",
"Guest 4 Birthdate",
"Guest 4 Postcode",
"Guest 5 First Name",
"Guest 5 Last Name",
"Guest 5 Email",
"Guest 5 Phone",
"Guest 5 Birthdate",
"Guest 5 Postcode",
"Guest 6 First Name",
"Guest 6 Last Name",
"Guest 6 Email",
"Guest 6 Phone",
"Guest 6 Birthdate",
"Guest 6 Postcode",
"Guest 7 First Name",
"Guest 7 Last Name",
"Guest 7 Email",
"Guest 7 Phone",
"Guest 7 Birthdate",
"Guest 7 Postcode",
"Guest 8 First Name",
"Guest 8 Last Name",
"Guest 8 Email",
"Guest 8 Phone",
"Guest 8 Birthdate",
"Guest 8 Postcode",
"Guest 9 First Name",
"Guest 9 Last Name",
"Guest 9 Email",
"Guest 9 Phone",
"Guest 9 Birthdate",
"Guest 9 Postcode",
"Payment Status",
"Paid At",
"Extra Questions",
@@ -740,24 +688,28 @@ export const appRouter = {
"Created At",
];
const MAX_GUESTS = 9;
const rows: string[][] = [];
const rows = data.map((r) => {
const guests = parseGuestsJson(r.guests);
for (const r of data) {
const guests = parseGuestsJson(r.guests) as Array<{
firstName: string;
lastName: string;
email?: string;
phone?: string;
birthdate?: string;
postcode?: string;
}>;
// Build guest columns (up to 9 guests, 6 fields each)
const guestCols: string[] = [];
for (let i = 0; i < MAX_GUESTS; i++) {
const g = guests[i];
guestCols.push(g?.firstName ?? "");
guestCols.push(g?.lastName ?? "");
guestCols.push(g?.email ?? "");
guestCols.push(g?.phone ?? "");
guestCols.push((g as { birthdate?: string })?.birthdate ?? "");
guestCols.push((g as { postcode?: string })?.postcode ?? "");
}
const paymentStatus =
r.paymentStatus === "paid"
? "Paid"
: r.paymentStatus === "extra_payment_pending"
? "Extra Payment Pending"
: "Pending";
return [
// Registrant row
rows.push([
"registrant",
r.id,
r.firstName,
r.lastName,
@@ -771,19 +723,39 @@ export const appRouter = {
r.isOver16 ? "Yes" : "No",
String(r.drinkCardValue ?? 0),
String(r.giftAmount ?? 0),
String(guests.length),
...guestCols,
r.paymentStatus === "paid"
? "Paid"
: r.paymentStatus === "extra_payment_pending"
? "Extra Payment Pending"
: "Pending",
paymentStatus,
r.paidAt ? r.paidAt.toISOString() : "",
r.extraQuestions || "",
r.cancelledAt ? r.cancelledAt.toISOString() : "",
r.createdAt.toISOString(),
];
});
]);
// One row per guest — link back to parent registration via Registration ID
for (const g of guests) {
rows.push([
"guest",
r.id,
g.firstName,
g.lastName,
g.email || "",
g.phone || "",
g.birthdate || "",
g.postcode || "",
// Registrant-only fields left blank
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
]);
}
}
const csvContent = [
headers.map(escapeCell).join(","),