chris a5775930aa Add photo ZIP downloads, HEIC/HEIF uploads, thank-you notes, ICS export, and theme color overrides
- Photos: guests and hosts can download all event photos as a ZIP;
  HEIC/HEIF uploads are now accepted, with a WebP thumbnail generated
  for gallery display while the original file is preserved for
  download; uploader name now always shows as a caption
- Hosts can send a thank-you note (email/SMS) to guests who RSVP'd yes
- Guests can add the event to Google Calendar or download an .ics file,
  and see a live countdown to the event
- Hosts can override a theme's primary/accent colors per event
- New textures for a few themes' card backgrounds
2026-07-19 18:23:05 -04:00

41 lines
1.3 KiB
TypeScript

import { NextResponse } from 'next/server';
import { prisma } from '@/lib/prisma';
import { requireEventAccess } from '@/lib/eventAccess';
// Quote every field per RFC 4180 and double up embedded quotes - simplest
// rule that's correct for names/notes containing commas, quotes, or newlines.
function csvField(value: string): string {
return `"${value.replace(/"/g, '""')}"`;
}
function csvRow(fields: string[]): string {
return fields.map(csvField).join(',') + '\r\n';
}
export async function GET(_request: Request, { params }: { params: Promise<{ id: string }> }) {
const { id } = await params;
const { event } = await requireEventAccess(id);
const rsvps = await prisma.rsvp.findMany({ where: { eventId: id }, orderBy: { createdAt: 'asc' } });
let csv = csvRow(['Name', 'Status', 'Guests', 'Email', 'Phone', 'Note', 'Responded at (UTC)']);
for (const rsvp of rsvps) {
csv += csvRow([
rsvp.guestName,
rsvp.status,
String(rsvp.numGuests),
rsvp.guestEmail ?? '',
rsvp.guestPhone ?? '',
rsvp.note,
rsvp.createdAt.toISOString(),
]);
}
return new NextResponse(csv, {
headers: {
'Content-Type': 'text/csv; charset=utf-8',
'Content-Disposition': `attachment; filename="${event.slug}-rsvps.csv"`,
},
});
}