Skip to content

Commit

Permalink
fix: repetive schedule creation
Browse files Browse the repository at this point in the history
  • Loading branch information
alifhaider committed Jan 7, 2025
1 parent ea08f23 commit b2f0c4b
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 52 deletions.
4 changes: 2 additions & 2 deletions app/routes/add.schedule.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,8 @@ export async function action({ request }: ActionFunctionArgs) {
const locationId = data.locationId

const scheduleDates = oneDay
? getMonthlyScheduleDates(oneDay, isRepetiveMonth)
: getWeeklyScheduleDates(weeklyDays, isRepetiveWeek)
? getMonthlyScheduleDates(oneDay, isRepetiveMonth ?? false)
: getWeeklyScheduleDates(weeklyDays, isRepetiveWeek ?? false)

const existingSchedules = await prisma.schedule.findMany({
where: {
Expand Down
6 changes: 3 additions & 3 deletions app/services/schedule.server.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,9 @@ describe('getWeeklyScheduleDates', () => {
false,
)
expect(result).toHaveLength(3)
expect(result[0].toISOString()).toBe('2024-09-08T00:00:00.000Z') // Next Sunday
expect(result[1].toISOString()).toBe('2024-09-04T00:00:00.000Z') // Same Wednesday
expect(result[2].toISOString()).toBe('2024-09-06T00:00:00.000Z') // Next Friday
expect(result[0].toISOString()).toBe('2024-09-04T00:00:00.000Z') // Same Wednesday
expect(result[1].toISOString()).toBe('2024-09-06T00:00:00.000Z') // Next Friday
expect(result[2].toISOString()).toBe('2024-09-08T00:00:00.000Z') // Next Sunday
})

it(`should return the next ${REPEAT_WEEKS} occurrences for a single day (repetitive)`, () => {
Expand Down
97 changes: 50 additions & 47 deletions app/services/schedule.server.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Schedule } from '@prisma/client'
import { add, getDay, addMonths, addWeeks } from 'date-fns'
import { addMonths, addDays } from 'date-fns'
import { DAYS } from '~/routes/add.schedule'
import { getHoursAndMinutes } from '~/utils/schedule'

Expand Down Expand Up @@ -27,57 +27,60 @@ export function getMonthlyScheduleDates(
)
}

function getDayByNumber(day: (typeof DAYS)[number]) {
const daysOfWeek = {
sunday: 0,
monday: 1,
tuesday: 2,
wednesday: 3,
thursday: 4,
friday: 5,
saturday: 6,
}
return daysOfWeek[day]
}

export function getWeeklyScheduleDates(
days?: TDay[],
isRepetitiveWeek?: boolean,
) {
if (!days) return []
export function getWeeklyScheduleDates(daysArray?: TDay[], isRepetive = false) {
if (!daysArray?.length) return []
const today = new Date()
const currentDayIndex = getDay(today)

// Calculate the next occurrence for each day
const nextOccurrences = days.map(day => {
const targetDayIndex = getDayByNumber(day)
let daysUntilNextOccurrence = (targetDayIndex - currentDayIndex + 7) % 7
if (daysUntilNextOccurrence === 0 && today.getDay() !== targetDayIndex) {
daysUntilNextOccurrence = 7
const dayNames = [
'sunday',
'monday',
'tuesday',
'wednesday',
'thursday',
'friday',
'saturday',
]

// Function to find the next occurrences for a given weekday name
const getOccurrences = (dayName: string) => {
const dayIndex = dayNames.indexOf(dayName.toLocaleLowerCase())
const occurrences = []
let currentDate = today

// Check if today matches the target day
if (currentDate.getDay() === dayIndex) {
occurrences.push(currentDate)
}
const nextDate = add(today, { days: daysUntilNextOccurrence })
nextDate.setUTCHours(0, 0, 0, 0)
return nextDate
})

if (!isRepetitiveWeek) return nextOccurrences

// Generate the next 52 occurrences for each selected day
const occurrences = days.flatMap(day => {
const targetDayIndex = getDayByNumber(day)
return Array.from({ length: REPEAT_WEEKS }, (_, week) => {
const baseDate = nextOccurrences.find(
occurrence => getDay(occurrence) === targetDayIndex,
)!
// Calculate the occurrence date for this week + week offset
return addWeeks(baseDate, week)
})
})
// Generate future occurrences
const targetOccurrences = isRepetive ? REPEAT_WEEKS : 1
while (occurrences.length < targetOccurrences) {
currentDate = addDays(currentDate, 1)

// Sort occurrences by date to ensure they're in the correct order
occurrences.sort((a, b) => a.getTime() - b.getTime())
if (currentDate.getDay() === dayIndex) {
occurrences.push(new Date(currentDate)) // Add occurrence
}

// Safety check to avoid infinite loop
if (occurrences.length > 366) {
console.error('Infinite loop detected')
break
}
}

return occurrences
}

// Generate all occurrences for the input days
const allOccurrences = daysArray.flatMap(dayName => getOccurrences(dayName))

// Remove duplicates, sort, and format the dates
const uniqueDates = Array.from(
new Set(allOccurrences.map(date => date.getTime())),
)
.sort((a, b) => a - b)
.map(date => new Date(date))

return occurrences
return uniqueDates
}

export function checkOverlapSchedule(
Expand Down
Binary file modified prisma/dev.db
Binary file not shown.

0 comments on commit b2f0c4b

Please sign in to comment.