feat(matching): add schedule config for division collision groups
Allow event organizers to configure which divisions run in parallel (same time slot) for accurate collision detection in the auto-matching algorithm. Divisions in the same slot will collide with each other. - Add scheduleConfig JSON field to Event model - Add PUT /events/:slug/schedule-config API endpoint - Update matching algorithm to use slot-based collision detection - Add UI in EventDetailsPage for managing division slots - Add unit tests for schedule-based collision detection
This commit is contained in:
@@ -381,6 +381,7 @@ router.get('/:slug/details', authenticate, async (req, res, next) => {
|
||||
description: event.description,
|
||||
registrationDeadline: event.registrationDeadline,
|
||||
matchingRunAt: event.matchingRunAt,
|
||||
scheduleConfig: event.scheduleConfig,
|
||||
},
|
||||
checkin: {
|
||||
token: checkinToken.token,
|
||||
@@ -914,6 +915,76 @@ router.delete('/:slug/heats/:id', authenticate, async (req, res, next) => {
|
||||
// AUTO-MATCHING ENDPOINTS
|
||||
// ============================================
|
||||
|
||||
// PUT /api/events/:slug/schedule-config - Set schedule configuration (division order and collision groups)
|
||||
router.put('/:slug/schedule-config', authenticate, async (req, res, next) => {
|
||||
try {
|
||||
const { slug } = req.params;
|
||||
const { scheduleConfig } = req.body;
|
||||
|
||||
// Validate schedule config structure
|
||||
if (scheduleConfig !== null && scheduleConfig !== undefined) {
|
||||
if (!scheduleConfig.slots || !Array.isArray(scheduleConfig.slots)) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: 'scheduleConfig must have a "slots" array',
|
||||
});
|
||||
}
|
||||
|
||||
// Validate each slot
|
||||
for (let i = 0; i < scheduleConfig.slots.length; i++) {
|
||||
const slot = scheduleConfig.slots[i];
|
||||
if (typeof slot.order !== 'number' || !Array.isArray(slot.divisionIds)) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: `Slot ${i} must have "order" (number) and "divisionIds" (array)`,
|
||||
});
|
||||
}
|
||||
|
||||
// Validate divisionIds are numbers
|
||||
if (!slot.divisionIds.every(id => typeof id === 'number')) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: `Slot ${i} divisionIds must be an array of numbers`,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find event
|
||||
const event = await prisma.event.findUnique({
|
||||
where: { slug },
|
||||
select: { id: true },
|
||||
});
|
||||
|
||||
if (!event) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
error: 'Event not found',
|
||||
});
|
||||
}
|
||||
|
||||
// Update schedule config
|
||||
const updated = await prisma.event.update({
|
||||
where: { id: event.id },
|
||||
data: {
|
||||
scheduleConfig: scheduleConfig || null,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
slug: true,
|
||||
scheduleConfig: true,
|
||||
},
|
||||
});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: updated,
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
// PUT /api/events/:slug/registration-deadline - Set registration deadline
|
||||
router.put('/:slug/registration-deadline', authenticate, async (req, res, next) => {
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user