279 lines
8.4 KiB
TypeScript
279 lines
8.4 KiB
TypeScript
import { DateTimeUtils } from '../common/utils/datetime.utils'
|
|
|
|
import { Injectable, Logger } from '@nestjs/common'
|
|
import { PrismaService } from 'src/Prisma/prisma.service'
|
|
|
|
import { AppConfigService } from 'src/AppConfig/appconfig.service'
|
|
import {
|
|
PreviewScheduleType,
|
|
ScheduleConfigType,
|
|
ScheduleConfigTypeForCenter,
|
|
ScheduleSlotType,
|
|
} from './schedule.d'
|
|
import { Config, Schedule, ScheduleDate } from '@prisma/client'
|
|
import { DateTime, Settings, Zone } from 'luxon'
|
|
import * as _ from 'lodash'
|
|
import { ScheduleDateInput } from './schedule'
|
|
|
|
@Injectable()
|
|
export class ScheduleService {
|
|
constructor(
|
|
private readonly prisma: PrismaService,
|
|
private readonly appConfigService: AppConfigService,
|
|
) {}
|
|
|
|
async createSchedulePreviewForSingleDay(
|
|
scheduleConfig: ScheduleConfigType,
|
|
): Promise<PreviewScheduleType> {
|
|
// generate Slot By config
|
|
const slots = this.generateSlots(scheduleConfig)
|
|
|
|
return {
|
|
totalSlots: slots.length,
|
|
slots: slots,
|
|
}
|
|
}
|
|
|
|
// create preview for center require scheduleConfigInput: { startDate: "2024-11-02T00:00:00.000Z", endDate: "2024-11-22T00:00:00.000Z", slots: [1, 3], days: [2, 5] }
|
|
async createSchedulePreviewForCenter(
|
|
scheduleConfig: ScheduleConfigTypeForCenter,
|
|
): Promise<PreviewScheduleType> {
|
|
const config: ScheduleConfigType = (
|
|
await this.appConfigService.getVisibleConfigs()
|
|
).reduce((acc, curr) => {
|
|
// @ts-ignore
|
|
acc[curr.key] = curr.value
|
|
return acc
|
|
}, {} as ScheduleConfigType)
|
|
const slots = this.generateSlotsPreviewForCenter(scheduleConfig, config)
|
|
return {
|
|
totalSlots: slots.length,
|
|
slots: slots,
|
|
}
|
|
}
|
|
|
|
async generateScheduleDates(schedule: Schedule): Promise<ScheduleDate[]> {
|
|
// generate schedule dates based on data and config
|
|
const config: ScheduleConfigType = (
|
|
await this.appConfigService.getVisibleConfigs()
|
|
).reduce((acc, curr) => {
|
|
// @ts-ignore
|
|
acc[curr.key] = curr.value
|
|
return acc
|
|
}, {} as ScheduleConfigType)
|
|
|
|
const daysOfWeeks = schedule.daysOfWeek
|
|
const slots = schedule.slots
|
|
const scheduleStart = DateTime.fromJSDate(schedule.scheduleStart)
|
|
const scheduleEnd = DateTime.fromJSDate(schedule.scheduleEnd)
|
|
const slotDuration = parseInt(config.slotDuration)
|
|
const slotBreakDuration = parseInt(config.slotBreakDuration)
|
|
// add participants to schedule dates
|
|
const scheduleDates: ScheduleDateInput[] = []
|
|
|
|
// loop each day from scheduleStart to scheduleEnd
|
|
for (
|
|
let date = scheduleStart;
|
|
date <= scheduleEnd;
|
|
date = date.plus({ days: 1 })
|
|
) {
|
|
// Check if the current date matches one of the specified days of the week
|
|
if (daysOfWeeks.includes(date.weekday)) {
|
|
// loop through slots
|
|
for (const slot of slots) {
|
|
const { startTime, endTime } = this.getSlotStartAndEndTime(
|
|
slot,
|
|
slotDuration.toString(),
|
|
slotBreakDuration.toString(),
|
|
DateTimeUtils.getATimeWithDateB(
|
|
DateTime.fromISO(config.dayStartTime),
|
|
date,
|
|
).toISO() ?? '',
|
|
)
|
|
scheduleDates.push({
|
|
scheduleId: schedule.id,
|
|
start: startTime.toISO() ?? '',
|
|
end: endTime.toISO() ?? '',
|
|
dayOfWeek: date.weekday,
|
|
slot: slot,
|
|
participantIds: [],
|
|
serviceId: schedule.managedServiceId,
|
|
orderId: schedule.orderId,
|
|
})
|
|
}
|
|
}
|
|
}
|
|
const scheduleDatesCreated =
|
|
await this.prisma.scheduleDate.createManyAndReturn({
|
|
data: scheduleDates,
|
|
})
|
|
|
|
return scheduleDatesCreated
|
|
}
|
|
|
|
/*
|
|
example query:
|
|
query CenterPreviewSchedule {
|
|
centerPreviewSchedule(
|
|
scheduleConfigInput: { days: [3,5], endDate: "2024-11-22T00:00:00.000Z", slots: [2,6], startDate: "2024-11-02T00:00:00.000Z" }
|
|
) {
|
|
totalSlots
|
|
slots {
|
|
dayOfWeek
|
|
end
|
|
slot
|
|
start
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
|
|
generateSlotsPreviewForCenter(
|
|
_scheduleConfig: ScheduleConfigTypeForCenter,
|
|
_config: ScheduleConfigType,
|
|
): ScheduleSlotType[] {
|
|
const slots: ScheduleSlotType[] = []
|
|
const daysOfWeeks = _scheduleConfig.days
|
|
const scheduleStart = DateTime.fromISO(_scheduleConfig.startDate)
|
|
const scheduleEnd = DateTime.fromISO(_scheduleConfig.endDate)
|
|
// loop each day from scheduleStart to scheduleEnd
|
|
for (
|
|
let date = scheduleStart;
|
|
date <= scheduleEnd;
|
|
date = date.plus({ days: 1 })
|
|
) {
|
|
// Check if the current date matches one of the specified days of the week
|
|
if (daysOfWeeks.includes(date.weekday)) {
|
|
// loop through slots
|
|
for (const slot of _scheduleConfig.slots) {
|
|
// get slot start and end time
|
|
const { startTime, endTime } = this.getSlotStartAndEndTime(
|
|
slot,
|
|
_config.slotDuration,
|
|
_config.slotBreakDuration,
|
|
DateTimeUtils.getATimeWithDateB(
|
|
DateTime.fromISO(_config.dayStartTime),
|
|
date,
|
|
).toISO() ?? '',
|
|
)
|
|
// if the slot is not overlapping with mid day break time, add it to the slots
|
|
if (
|
|
!DateTimeUtils.isOverlap(
|
|
startTime,
|
|
endTime,
|
|
DateTimeUtils.fromIsoString(_config.midDayBreakTimeStart),
|
|
DateTimeUtils.fromIsoString(_config.midDayBreakTimeEnd),
|
|
)
|
|
) {
|
|
slots.push({
|
|
slot: slot.toString(),
|
|
start: startTime.toString(),
|
|
end: endTime.toString(),
|
|
dayOfWeek: date.weekday,
|
|
})
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return slots
|
|
}
|
|
|
|
generateSlots(scheduleConfig: ScheduleConfigType): ScheduleSlotType[] {
|
|
const slots: ScheduleSlotType[] = []
|
|
const numberOfSlots = this.calculateNumberOfSlots(
|
|
scheduleConfig.dayStartTime,
|
|
scheduleConfig.dayEndTime,
|
|
scheduleConfig.slotDuration,
|
|
scheduleConfig.slotBreakDuration,
|
|
)
|
|
for (let i = 1; i <= numberOfSlots; i++) {
|
|
const { startTime, endTime } = this.getSlotStartAndEndTime(
|
|
i,
|
|
scheduleConfig.slotDuration,
|
|
scheduleConfig.slotBreakDuration,
|
|
scheduleConfig.dayStartTime,
|
|
)
|
|
// if the slot is not overlapping with mid day break time, add it to the slots
|
|
if (
|
|
!DateTimeUtils.isOverlap(
|
|
startTime,
|
|
endTime,
|
|
DateTimeUtils.fromIsoString(scheduleConfig.midDayBreakTimeStart),
|
|
DateTimeUtils.fromIsoString(scheduleConfig.midDayBreakTimeEnd),
|
|
)
|
|
) {
|
|
slots.push({
|
|
slot: i.toString(),
|
|
start: startTime.toString(),
|
|
end: endTime.toString(),
|
|
dayOfWeek: startTime.weekday,
|
|
})
|
|
}
|
|
}
|
|
return slots
|
|
}
|
|
|
|
isOverLapping(
|
|
startTime1: DateTime,
|
|
endTime1: DateTime,
|
|
startTime2: DateTime,
|
|
endTime2: DateTime,
|
|
) {
|
|
return (
|
|
Math.max(startTime1.toMillis(), startTime2.toMillis()) <
|
|
Math.min(endTime1.toMillis(), endTime2.toMillis())
|
|
)
|
|
}
|
|
|
|
calculateNumberOfSlots(
|
|
startTime: string,
|
|
endTime: string,
|
|
slotDuration: string,
|
|
slotBreakDuration: string,
|
|
) {
|
|
const _startTime = DateTimeUtils.toTime(startTime)
|
|
const _endTime = DateTimeUtils.toTime(endTime)
|
|
const _slotDuration = parseInt(slotDuration) // minutes
|
|
const _slotBreakDuration = parseInt(slotBreakDuration) // minutes
|
|
|
|
const startDate = DateTime.fromObject({
|
|
hour: _startTime.hour,
|
|
minute: _startTime.minute,
|
|
second: _startTime.second,
|
|
})
|
|
const endDate = DateTime.fromObject({
|
|
hour: _endTime.hour,
|
|
minute: _endTime.minute,
|
|
second: _endTime.second,
|
|
})
|
|
|
|
const totalMinutes =
|
|
(endDate.toMillis() - startDate.toMillis()) / (60 * 1000)
|
|
const numberOfSlots = Math.floor(
|
|
totalMinutes / (_slotDuration + _slotBreakDuration),
|
|
)
|
|
return numberOfSlots
|
|
}
|
|
|
|
getSlotStartAndEndTime(
|
|
slotNumber: number,
|
|
slotDuration: string,
|
|
slotBreakDuration: string,
|
|
dayStartTime: string,
|
|
) {
|
|
const durationInMinutes = parseInt(slotDuration)
|
|
const breakDurationInMinutes = parseInt(slotBreakDuration)
|
|
const initialStartTime = DateTime.fromISO(dayStartTime)
|
|
|
|
const startTime = initialStartTime.plus({
|
|
minutes: (slotNumber - 1) * (durationInMinutes + breakDurationInMinutes),
|
|
})
|
|
|
|
const endTime = startTime.plus({ minutes: durationInMinutes })
|
|
return {
|
|
startTime: startTime,
|
|
endTime: endTime,
|
|
}
|
|
}
|
|
}
|