feat(admin): improve export to put guests in their own row
This commit is contained in:
@@ -663,76 +663,24 @@ export const appRouter = {
|
|||||||
|
|
||||||
const escapeCell = (val: string) => `"${val.replace(/"/g, '""')}"`;
|
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 = [
|
const headers = [
|
||||||
"ID",
|
"Row Type", // "registrant" | "guest"
|
||||||
|
"Registration ID", // UUID of the parent registration (same for registrant + its guests)
|
||||||
"First Name",
|
"First Name",
|
||||||
"Last Name",
|
"Last Name",
|
||||||
"Email",
|
"Email",
|
||||||
"Phone",
|
"Phone",
|
||||||
"Birthdate",
|
"Birthdate",
|
||||||
"Postcode",
|
"Postcode",
|
||||||
|
// Registrant-only fields (blank for guest rows)
|
||||||
"Type",
|
"Type",
|
||||||
"Art Form",
|
"Art Form",
|
||||||
"Experience",
|
"Experience",
|
||||||
"Is Over 16",
|
"Is Over 16",
|
||||||
"Drink Card Value (EUR)",
|
"Drink Card Value (EUR)",
|
||||||
"Gift Amount (cents)",
|
"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",
|
"Payment Status",
|
||||||
"Paid At",
|
"Paid At",
|
||||||
"Extra Questions",
|
"Extra Questions",
|
||||||
@@ -740,24 +688,28 @@ export const appRouter = {
|
|||||||
"Created At",
|
"Created At",
|
||||||
];
|
];
|
||||||
|
|
||||||
const MAX_GUESTS = 9;
|
const rows: string[][] = [];
|
||||||
|
|
||||||
const rows = data.map((r) => {
|
for (const r of data) {
|
||||||
const guests = parseGuestsJson(r.guests);
|
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 paymentStatus =
|
||||||
const guestCols: string[] = [];
|
r.paymentStatus === "paid"
|
||||||
for (let i = 0; i < MAX_GUESTS; i++) {
|
? "Paid"
|
||||||
const g = guests[i];
|
: r.paymentStatus === "extra_payment_pending"
|
||||||
guestCols.push(g?.firstName ?? "");
|
? "Extra Payment Pending"
|
||||||
guestCols.push(g?.lastName ?? "");
|
: "Pending";
|
||||||
guestCols.push(g?.email ?? "");
|
|
||||||
guestCols.push(g?.phone ?? "");
|
|
||||||
guestCols.push((g as { birthdate?: string })?.birthdate ?? "");
|
|
||||||
guestCols.push((g as { postcode?: string })?.postcode ?? "");
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
// Registrant row
|
||||||
|
rows.push([
|
||||||
|
"registrant",
|
||||||
r.id,
|
r.id,
|
||||||
r.firstName,
|
r.firstName,
|
||||||
r.lastName,
|
r.lastName,
|
||||||
@@ -771,19 +723,39 @@ export const appRouter = {
|
|||||||
r.isOver16 ? "Yes" : "No",
|
r.isOver16 ? "Yes" : "No",
|
||||||
String(r.drinkCardValue ?? 0),
|
String(r.drinkCardValue ?? 0),
|
||||||
String(r.giftAmount ?? 0),
|
String(r.giftAmount ?? 0),
|
||||||
String(guests.length),
|
paymentStatus,
|
||||||
...guestCols,
|
|
||||||
r.paymentStatus === "paid"
|
|
||||||
? "Paid"
|
|
||||||
: r.paymentStatus === "extra_payment_pending"
|
|
||||||
? "Extra Payment Pending"
|
|
||||||
: "Pending",
|
|
||||||
r.paidAt ? r.paidAt.toISOString() : "",
|
r.paidAt ? r.paidAt.toISOString() : "",
|
||||||
r.extraQuestions || "",
|
r.extraQuestions || "",
|
||||||
r.cancelledAt ? r.cancelledAt.toISOString() : "",
|
r.cancelledAt ? r.cancelledAt.toISOString() : "",
|
||||||
r.createdAt.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 = [
|
const csvContent = [
|
||||||
headers.map(escapeCell).join(","),
|
headers.map(escapeCell).join(","),
|
||||||
|
|||||||
Reference in New Issue
Block a user