invite/src/app/dashboard/page.tsx
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

91 lines
3.3 KiB
TypeScript

import Link from 'next/link';
import { prisma } from '@/lib/prisma';
import { requireUser } from '@/lib/session';
import { resolveTheme } from '@/lib/themes';
import { PageContainer } from '@/components/PageContainer';
export default async function DashboardPage() {
const user = await requireUser();
const events = await prisma.event.findMany({
where: {
OR: [{ hostId: user.id }, { collaborators: { some: { userId: user.id, status: 'ACCEPTED' } } }],
},
orderBy: { eventDate: 'asc' },
include: {
_count: { select: { comments: true } },
rsvps: { select: { status: true, numGuests: true } },
},
});
return (
<PageContainer>
<div className="flex items-center justify-between">
<h1 className="text-2xl font-bold text-gray-900">Your events</h1>
<Link
href="/dashboard/events/new"
className="rounded-md bg-indigo-600 px-4 py-2 text-sm font-medium text-white hover:bg-indigo-500"
>
New event
</Link>
</div>
{events.length === 0 ? (
<p className="mt-8 text-gray-600">
You haven't created any events yet.{' '}
<Link href="/dashboard/events/new" className="font-medium text-indigo-600 hover:text-indigo-500">
Create your first invite
</Link>
.
</p>
) : (
<ul className="mt-6 flex flex-col gap-3">
{events.map((event) => {
const theme = resolveTheme(event.theme, event.themePrimaryColor, event.themeAccentColor);
const yesCount = event.rsvps
.filter((r) => r.status === 'YES')
.reduce((sum, r) => sum + r.numGuests, 0);
return (
<li key={event.id}>
<Link
href={`/dashboard/events/${event.id}/guests`}
className="flex items-center justify-between rounded-lg border bg-white px-5 py-4 hover:border-indigo-300"
>
<div className="flex items-center gap-3">
<span
className="flex h-10 w-10 items-center justify-center rounded-full text-lg"
style={{ background: theme.colors.bg }}
>
{theme.emoji}
</span>
<div>
<p className="font-medium text-gray-900">
{event.title}
{event.hostId !== user.id && (
<span className="ml-2 rounded-full bg-gray-100 px-2 py-0.5 text-xs font-medium text-gray-500">
Co-hosting
</span>
)}
</p>
<p className="text-sm text-gray-500">
{event.eventDate.toLocaleDateString('en-US', {
dateStyle: 'medium',
timeZone: event.timezone,
})}
</p>
</div>
</div>
<div className="text-right text-sm text-gray-500">
<p>{yesCount} attending</p>
<p>{event._count.comments} comments</p>
</div>
</Link>
</li>
);
})}
</ul>
)}
</PageContainer>
);
}