feat(events): markdown body, registered indicator, drop capacity counters

- Public event detail page renders description/series-description/content
  as markdown via useMarkdown, with prose styles; agenda becomes an
  unordered list with the same custom bullets.
- Events list API returns `isRegistered` per event (derived from the
  requester's registrations, ignoring cancelled rows), and the list page
  shows a "Registered" tag. Stops exposing the full registrations array
  in the list response.
- Removes the seats/sold-out/limited capacity UI from list and detail
  pages.
- EventTicketPurchase: minor template formatting (self-closing inputs,
  prettier wrapping).
This commit is contained in:
Jennie Robinson Faber 2026-05-21 17:50:48 +01:00
parent 2ffaf0ef09
commit 622cc8e53b
4 changed files with 176 additions and 118 deletions

View file

@ -35,17 +35,30 @@ export default defineEventHandler(async (event) => {
}
// Fetch events from database
const events = await Event.find(filter)
.sort({ startDate: 1 })
.select("-registrations") // Don't expose registration details in list view
.lean();
const events = await Event.find(filter).sort({ startDate: 1 }).lean();
// Add computed fields
const eventsWithMeta = events.map((event) => ({
...event,
id: event._id.toString(),
registeredCount: event.registrations?.length || 0,
}));
const requesterEmail = requester?.email?.toLowerCase();
const requesterId = requester?._id?.toString();
// Add computed fields; strip registrations from the response.
const eventsWithMeta = events.map((event) => {
const regs = event.registrations || [];
const isRegistered =
!!requester &&
regs.some(
(r) =>
!r.cancelledAt &&
((r.memberId && r.memberId.toString() === requesterId) ||
(r.email && r.email.toLowerCase() === requesterEmail)),
);
const { registrations, ...rest } = event;
return {
...rest,
id: event._id.toString(),
registeredCount: regs.length,
isRegistered,
};
});
return eventsWithMeta;
} catch (error) {