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:
Michilis
2026-02-12 03:01:58 +00:00
parent 8315029091
commit fe75912f23
8 changed files with 61 additions and 68 deletions

View File

@@ -4,7 +4,7 @@ import { z } from 'zod';
import { db, dbGet, dbAll, events, tickets, payments, eventPaymentOverrides, emailLogs, invoices, siteSettings } from '../db/index.js';
import { eq, desc, and, gte, sql } from 'drizzle-orm';
import { requireAuth, getAuthUser } from '../lib/auth.js';
import { generateId, getNow, convertBooleansForDb, toDbDate } from '../lib/utils.js';
import { generateId, getNow, convertBooleansForDb, toDbDate, calculateAvailableSeats } from '../lib/utils.js';
interface UserContext {
id: string;
@@ -151,10 +151,11 @@ eventsRouter.get('/', async (c) => {
);
const normalized = normalizeEvent(event);
const bookedCount = ticketCount?.count || 0;
return {
...normalized,
bookedCount: ticketCount?.count || 0,
availableSeats: normalized.capacity - (ticketCount?.count || 0),
bookedCount,
availableSeats: calculateAvailableSeats(normalized.capacity, bookedCount),
};
})
);
@@ -189,11 +190,12 @@ eventsRouter.get('/:id', async (c) => {
);
const normalized = normalizeEvent(event);
const bookedCount = ticketCount?.count || 0;
return c.json({
event: {
...normalized,
bookedCount: ticketCount?.count || 0,
availableSeats: normalized.capacity - (ticketCount?.count || 0),
bookedCount,
availableSeats: calculateAvailableSeats(normalized.capacity, bookedCount),
},
});
});
@@ -277,7 +279,7 @@ eventsRouter.get('/next/upcoming', async (c) => {
event: {
...normalized,
bookedCount,
availableSeats: normalized.capacity - bookedCount,
availableSeats: calculateAvailableSeats(normalized.capacity, bookedCount),
isFeatured: true,
},
});
@@ -308,7 +310,7 @@ eventsRouter.get('/next/upcoming', async (c) => {
event: {
...normalized,
bookedCount,
availableSeats: normalized.capacity - bookedCount,
availableSeats: calculateAvailableSeats(normalized.capacity, bookedCount),
isFeatured: false,
},
});