Fix event capacity: no negative availability, sold-out enforcement, admin override
- Backend: use calculateAvailableSeats so availableSeats is never negative - Backend: reject public booking when confirmed >= capacity; admin create/manual bypass capacity - Frontend: spotsLeft = max(0, capacity - bookedCount), isSoldOut when bookedCount >= capacity - Frontend: sold-out redirect on booking page, cap quantity by spotsLeft, never show negative Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -41,8 +41,10 @@ export default function EventDetailClient({ eventId, initialEvent }: EventDetail
|
||||
.catch(console.error);
|
||||
}, [eventId]);
|
||||
|
||||
// Max tickets is remaining capacity
|
||||
const maxTickets = Math.max(1, event.availableSeats || 1);
|
||||
// Spots left: never negative; sold out when confirmed >= capacity
|
||||
const spotsLeft = Math.max(0, event.capacity - (event.bookedCount ?? 0));
|
||||
const isSoldOut = (event.bookedCount ?? 0) >= event.capacity;
|
||||
const maxTickets = isSoldOut ? 0 : Math.max(1, spotsLeft);
|
||||
|
||||
const decreaseQuantity = () => {
|
||||
setTicketQuantity(prev => Math.max(1, prev - 1));
|
||||
@@ -68,7 +70,6 @@ export default function EventDetailClient({ eventId, initialEvent }: EventDetail
|
||||
});
|
||||
};
|
||||
|
||||
const isSoldOut = event.availableSeats === 0;
|
||||
const isCancelled = event.status === 'cancelled';
|
||||
// Only calculate isPastEvent after mount to avoid hydration mismatch
|
||||
const isPastEvent = mounted ? new Date(event.startDatetime) < new Date() : false;
|
||||
@@ -154,7 +155,7 @@ export default function EventDetailClient({ eventId, initialEvent }: EventDetail
|
||||
|
||||
{!event.externalBookingEnabled && (
|
||||
<p className="mt-4 text-center text-sm text-gray-500">
|
||||
{event.availableSeats} {t('events.details.spotsLeft')}
|
||||
{spotsLeft} / {event.capacity} {t('events.details.spotsLeft')}
|
||||
</p>
|
||||
)}
|
||||
</>
|
||||
@@ -257,7 +258,7 @@ export default function EventDetailClient({ eventId, initialEvent }: EventDetail
|
||||
<div>
|
||||
<p className="font-medium text-sm">{t('events.details.capacity')}</p>
|
||||
<p className="text-gray-600">
|
||||
{event.availableSeats} / {event.capacity} {t('events.details.spotsLeft')}
|
||||
{spotsLeft} / {event.capacity} {t('events.details.spotsLeft')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user