188 lines
6.9 KiB
JavaScript
188 lines
6.9 KiB
JavaScript
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.ShiftSeriesService = void 0;
|
|
const database_1 = require("../../../config/database");
|
|
const client_1 = require("@prisma/client");
|
|
const MAX_OCCURRENCES = 100;
|
|
const MAX_DAYS = 365;
|
|
class ShiftSeriesService {
|
|
/**
|
|
* Generate occurrence dates based on recurrence rules
|
|
*/
|
|
static generateOccurrences(input) {
|
|
const dates = [];
|
|
const startDate = new Date(input.startDate);
|
|
const endDate = input.endDate ? new Date(input.endDate) : null;
|
|
let currentDate = new Date(startDate);
|
|
let count = 0;
|
|
while (count < MAX_OCCURRENCES) {
|
|
// Check end date
|
|
if (endDate && currentDate > endDate)
|
|
break;
|
|
// Check max days
|
|
const daysDiff = Math.floor((currentDate.getTime() - startDate.getTime()) / (1000 * 60 * 60 * 24));
|
|
if (daysDiff > MAX_DAYS)
|
|
break;
|
|
// Check if date matches recurrence pattern
|
|
let shouldInclude = false;
|
|
if (input.frequency === client_1.RecurrenceFrequency.DAILY) {
|
|
shouldInclude = true;
|
|
}
|
|
else if (input.frequency === client_1.RecurrenceFrequency.WEEKLY) {
|
|
const dayOfWeek = currentDate.getDay(); // 0=Sun, 6=Sat
|
|
shouldInclude = input.daysOfWeek?.includes(dayOfWeek) ?? false;
|
|
}
|
|
else if (input.frequency === client_1.RecurrenceFrequency.MONTHLY) {
|
|
shouldInclude = currentDate.getDate() === startDate.getDate();
|
|
}
|
|
if (shouldInclude) {
|
|
dates.push(new Date(currentDate));
|
|
count++;
|
|
}
|
|
// Increment date
|
|
if (input.frequency === client_1.RecurrenceFrequency.DAILY) {
|
|
currentDate.setDate(currentDate.getDate() + 1);
|
|
}
|
|
else if (input.frequency === client_1.RecurrenceFrequency.WEEKLY) {
|
|
currentDate.setDate(currentDate.getDate() + 1); // Check each day
|
|
}
|
|
else if (input.frequency === client_1.RecurrenceFrequency.MONTHLY) {
|
|
currentDate.setMonth(currentDate.getMonth() + 1);
|
|
}
|
|
}
|
|
return dates;
|
|
}
|
|
/**
|
|
* Create a shift series and generate all individual shifts
|
|
*/
|
|
static async createSeries(input, createdBy) {
|
|
// Generate occurrence dates
|
|
const dates = this.generateOccurrences(input);
|
|
if (dates.length === 0) {
|
|
throw new Error('No valid dates generated from recurrence rules');
|
|
}
|
|
// Create series and shifts in transaction
|
|
const result = await database_1.prisma.$transaction(async (tx) => {
|
|
// Create series
|
|
const series = await tx.shiftSeries.create({
|
|
data: {
|
|
title: input.title,
|
|
description: input.description,
|
|
startTime: input.startTime,
|
|
endTime: input.endTime,
|
|
location: input.location,
|
|
maxVolunteers: input.maxVolunteers,
|
|
isPublic: input.isPublic ?? false,
|
|
cutId: input.cutId,
|
|
frequency: input.frequency,
|
|
daysOfWeek: input.daysOfWeek,
|
|
startDate: new Date(input.startDate),
|
|
endDate: input.endDate ? new Date(input.endDate) : null,
|
|
createdBy,
|
|
},
|
|
});
|
|
// Create individual shifts
|
|
const shifts = await Promise.all(dates.map((date) => tx.shift.create({
|
|
data: {
|
|
title: input.title,
|
|
description: input.description,
|
|
date,
|
|
startTime: input.startTime,
|
|
endTime: input.endTime,
|
|
location: input.location,
|
|
maxVolunteers: input.maxVolunteers,
|
|
isPublic: input.isPublic ?? false,
|
|
cutId: input.cutId,
|
|
seriesId: series.id,
|
|
createdBy,
|
|
},
|
|
})));
|
|
return { series, shifts };
|
|
});
|
|
return {
|
|
series: result.series,
|
|
generatedShiftsCount: result.shifts.length,
|
|
};
|
|
}
|
|
/**
|
|
* Get count of non-exception shifts in a series
|
|
*/
|
|
static async getShiftCount(seriesId) {
|
|
return database_1.prisma.shift.count({
|
|
where: { seriesId, isException: false },
|
|
});
|
|
}
|
|
/**
|
|
* Get series with all its shifts
|
|
*/
|
|
static async getSeries(seriesId) {
|
|
const series = await database_1.prisma.shiftSeries.findUnique({
|
|
where: { id: seriesId },
|
|
include: {
|
|
cut: true,
|
|
shifts: {
|
|
orderBy: { date: 'asc' },
|
|
include: { signups: true },
|
|
},
|
|
},
|
|
});
|
|
if (!series) {
|
|
throw new Error('Shift series not found');
|
|
}
|
|
return series;
|
|
}
|
|
/**
|
|
* Update series shifts based on edit mode
|
|
*/
|
|
static async updateSeries(seriesId, input) {
|
|
const series = await database_1.prisma.shiftSeries.findUnique({
|
|
where: { id: seriesId },
|
|
include: { shifts: true },
|
|
});
|
|
if (!series) {
|
|
throw new Error('Shift series not found');
|
|
}
|
|
const { editMode, fromDate, ...updates } = input;
|
|
if (editMode === 'ALL') {
|
|
// Update series + all shifts
|
|
await database_1.prisma.$transaction(async (tx) => {
|
|
await tx.shiftSeries.update({
|
|
where: { id: seriesId },
|
|
data: updates,
|
|
});
|
|
await tx.shift.updateMany({
|
|
where: { seriesId, isException: false },
|
|
data: updates,
|
|
});
|
|
});
|
|
}
|
|
else if (editMode === 'FUTURE' && fromDate) {
|
|
// Update shifts from date onwards
|
|
await database_1.prisma.shift.updateMany({
|
|
where: {
|
|
seriesId,
|
|
date: { gte: new Date(fromDate) },
|
|
isException: false,
|
|
},
|
|
data: updates,
|
|
});
|
|
}
|
|
// Note: THIS mode is handled by regular shift update with isException flag
|
|
return this.getSeries(seriesId);
|
|
}
|
|
/**
|
|
* Delete series (cascades to shifts via onDelete: SetNull)
|
|
*/
|
|
static async deleteSeries(seriesId) {
|
|
// Delete all shifts in series first
|
|
await database_1.prisma.shift.deleteMany({
|
|
where: { seriesId },
|
|
});
|
|
// Then delete series
|
|
await database_1.prisma.shiftSeries.delete({
|
|
where: { id: seriesId },
|
|
});
|
|
}
|
|
}
|
|
exports.ShiftSeriesService = ShiftSeriesService;
|
|
//# sourceMappingURL=shift-series.service.js.map
|