The recurring events feature allows Events Office users to create gym sessions and professors to create workshops on a weekly recurring schedule. Instead of creating individual events one by one, users can specify a pattern (day of week, start date, end date) and all occurrences are created automatically.
Tracks recurring event series metadata and links to all created occurrences.
Fields:
eventType: 'gym_session' | 'workshop' - Type of recurring eventstartDate: Date - First date of the series (inclusive)endDate: Date - Last date to include (inclusive)dayOfWeek: String - Target day ('Monday', 'Tuesday', etc.)gymSessionData: Object - Template data for gym sessionstype: 'yoga' | 'pilates' | 'aerobics' | 'zumba' | 'cross_circuit' | 'kick_boxing'time: HH:MM format stringduration: Number (minutes)maxParticipants: Number
workshopData: Object - Template data for workshopsname,location,startTime,endTimeshortDescription,fullAgenda,facultyprofessors: Array of stringsrequiredBudget,fundingSource,extraResourcescapacity,registrationDeadline
createdEvents: Array - Links to created event documentseventId: ObjectId of created GymSession or WorkshopeventDate: Date of the eventstatus: 'created' | 'deleted'
creator: ObjectId - Reference to User who created the seriestotalOccurrences: Number - Count of occurrences createdstatus: 'active' | 'cancelled' - Series status
Added field:
recurringEventId: ObjectId (optional) - Reference to RecurringEvent if part of a series
Added field:
recurringEventId: ObjectId (optional) - Reference to RecurringEvent if part of a series
POST /api/facilities/gymsessions/recurring
Authorization: Bearer <token>
Role Required: events_office
Request Body:
{
"type": "yoga",
"time": "09:00",
"duration": 60,
"maxParticipants": 20,
"startDate": "2025-01-15T00:00:00Z",
"endDate": "2025-04-30T00:00:00Z",
"dayOfWeek": "Monday"
}Response (201 Created):
{
"status": "success",
"data": {
"recurringEvent": {
"id": "650abc123def456",
"eventType": "gym_session",
"pattern": {
"dayOfWeek": "Monday",
"startDate": "2025-01-15T00:00:00Z",
"endDate": "2025-04-30T00:00:00Z"
},
"totalOccurrences": 15,
"sessions": [
{
"_id": "650abc123def457",
"date": "2025-01-20T00:00:00Z",
"time": "09:00",
"duration": 60,
"type": "yoga",
"maxParticipants": 20,
"currentRegistrations": 0,
"creator": "650abc123def000"
},
// ... more sessions
]
}
}
}Error Responses:
- 400: Invalid input (validation errors, date ranges, etc.)
- 403: User is not Events Office
- 500: Server error
Example Usage:
// Create 15 Yoga sessions every Monday from Jan 15 to Apr 30, 2025
const response = await fetch('/api/facilities/gymsessions/recurring', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify({
type: 'yoga',
time: '09:00',
duration: 60,
maxParticipants: 20,
startDate: '2025-01-15',
endDate: '2025-04-30',
dayOfWeek: 'Monday'
})
});POST /api/workshops/recurring
Authorization: Bearer <token>
Role Required: professor
Request Body:
{
"name": "Python Advanced Concepts",
"location": "GUC Cairo",
"startTime": "14:00",
"endTime": "16:00",
"shortDescription": "Advanced Python programming course",
"fullAgenda": "Decorators, metaclasses, async/await patterns...",
"faculty": "Engineering",
"professors": ["Dr. Ahmed"],
"requiredBudget": 1000,
"fundingSource": "GUC",
"extraResources": "Laptops, projector",
"capacity": 30,
"registrationDeadline": "2025-01-13T00:00:00Z",
"startDate": "2025-01-15T00:00:00Z",
"endDate": "2025-04-30T00:00:00Z",
"dayOfWeek": "Friday"
}Response (201 Created):
{
"status": "success",
"data": {
"recurringEvent": {
"id": "650abc123def789",
"eventType": "workshop",
"pattern": {
"dayOfWeek": "Friday",
"startDate": "2025-01-15T00:00:00Z",
"endDate": "2025-04-30T00:00:00Z"
},
"totalOccurrences": 13,
"workshops": [
{
"_id": "650abc123def790",
"name": "Python Advanced Concepts",
"location": "GUC Cairo",
"startAt": "2025-01-17T00:00:00Z",
"endAt": "2025-01-18T00:00:00Z",
"startTime": "14:00",
"endTime": "16:00",
"shortDescription": "Advanced Python programming course",
"faculty": "Engineering",
"professors": ["Dr. Ahmed", "Prof. Name"],
"capacity": 30,
"status": "pending",
"creator": "650abc123def111"
},
// ... more workshops
]
}
}
}Error Responses:
- 400: Invalid input or date validation
- 403: User is not a professor
- 500: Server error
Example Usage:
// Create 13 workshops every Friday from Jan 15 to Apr 30, 2025
const response = await fetch('/api/workshops/recurring', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify({
name: 'Python Advanced Concepts',
location: 'GUC Cairo',
startTime: '14:00',
endTime: '16:00',
shortDescription: 'Advanced Python programming course',
fullAgenda: 'Detailed agenda here...',
faculty: 'Engineering',
professors: ['Dr. Ahmed'],
requiredBudget: 1000,
fundingSource: 'GUC',
capacity: 30,
registrationDeadline: '2025-01-13',
startDate: '2025-01-15',
endDate: '2025-04-30',
dayOfWeek: 'Friday'
})
});Calculates all dates matching a weekly pattern.
Parameters:
startDate: Date - Start date (inclusive)endDate: Date - End date (inclusive)dayOfWeek: String - Day name ('Monday', 'Tuesday', etc.)
Returns:
- Array of Date objects
Example:
const { calculateRecurringDates } = require('../utils/recurringEventUtils');
const dates = calculateRecurringDates(
new Date('2025-01-15'),
new Date('2025-04-30'),
'Monday'
);
// Returns: [2025-01-20, 2025-01-27, 2025-02-03, ...]Validates recurring event parameters.
Parameters:
params: Object withstartDate,endDate,dayOfWeek
Returns:
{ valid: true }or{ valid: false, error: string }
Deletes all gym sessions in a recurring series.
Parameters:
recurringEventId: ObjectId
Returns:
{ success: true, deletedCount: number }or{ success: false, error: string }
Deletes all workshops in a recurring series.
Parameters:
recurringEventId: ObjectId
Returns:
{ success: true, deletedCount: number }or{ success: false, error: string }
Gets recurring event details with all related events.
Parameters:
recurringEventId: ObjectId
Returns:
{ success: true, data: { recurringEvent, relatedEvents, count } }or error
- startDate & endDate: ISO 8601 format, startDate must be before endDate
- dayOfWeek: One of ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']
- type (gym): One of ['yoga', 'pilates', 'aerobics', 'zumba', 'cross_circuit', 'kick_boxing']
- time (gym): HH:MM format (00:00 - 23:59)
- duration: Integer >= 30 minutes
- maxParticipants: Integer >= 1
- capacity (workshop): Integer >= 1
- registrationDeadline (workshop): Must be before startAt
- Events Office only: Can create recurring gym sessions
- Professors only: Can create recurring workshops
- Each individual event can be edited/deleted independently
- Deleting a recurring series cancels all occurrences
const sessions = await GymSession.find({ recurringEventId });const workshops = await Workshop.find({ recurringEventId });const recurring = await RecurringEvent.findById(id)
.populate('creator', 'firstName lastName email');- Each created event (GymSession or Workshop) is a separate document
- Events can be edited or deleted individually without affecting the series
- Deleting the RecurringEvent marks all linked events as deleted
- The system supports weekly patterns only (no daily, monthly, custom patterns)
- All times are stored in UTC, conversion to local time happens on frontend