Add unlisted event status: hidden from listings but accessible by URL

- Backend: add 'unlisted' to schema enum and Zod validation; allow booking for unlisted events
- Frontend: Event type and guards updated; unlisted events bookable, excluded from public listing/sitemap
- Admin: badge, status dropdown, Make Unlisted / Make Public / Unpublish actions; scanner/emails/tickets include unlisted

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Michilis
2026-02-19 02:21:41 +00:00
parent 958181e049
commit bbfaa1172a
11 changed files with 48 additions and 16 deletions

View File

@@ -145,7 +145,7 @@ export default function BookingPage() {
paymentOptionsApi.getForEvent(params.eventId as string),
])
.then(([eventRes, paymentRes]) => {
if (!eventRes.event || eventRes.event.status !== 'published') {
if (!eventRes.event || !['published', 'unlisted'].includes(eventRes.event.status)) {
toast.error('Event is not available for booking');
router.push('/events');
return;

View File

@@ -60,7 +60,7 @@ export default function EventDetailClient({ eventId, initialEvent }: EventDetail
const isCancelled = event.status === 'cancelled';
// Only calculate isPastEvent after mount to avoid hydration mismatch
const isPastEvent = mounted ? new Date(event.startDatetime) < new Date() : false;
const canBook = !isSoldOut && !isCancelled && !isPastEvent && event.status === 'published';
const canBook = !isSoldOut && !isCancelled && !isPastEvent && (event.status === 'published' || event.status === 'unlisted');
// Booking card content - reused for mobile and desktop positions
const BookingCardContent = () => (

View File

@@ -20,7 +20,7 @@ interface Event {
price: number;
currency: string;
capacity: number;
status: 'draft' | 'published' | 'cancelled' | 'completed' | 'archived';
status: 'draft' | 'published' | 'unlisted' | 'cancelled' | 'completed' | 'archived';
bannerUrl?: string;
availableSeats?: number;
bookedCount?: number;