• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

tomosterlund / qalendar / 13467125422

21 Feb 2025 11:41PM UTC coverage: 97.288%. Remained the same
13467125422

Pull #269

github

web-flow
Merge 8ec4f6867 into 1e81b86d4
Pull Request #269: chore(deps): update dependency @types/node to v22

914 of 979 branches covered (93.36%)

Branch coverage included in aggregate %.

6439 of 6579 relevant lines covered (97.87%)

87.81 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.21
/src/helpers/EventPosition.ts
1
/**
1✔
2
 * The following class contains methods for calculating where to position
1✔
3
 * calendar events within a given day
1✔
4
 * */
1✔
5
import type {eventInterface} from '../typings/interfaces/event.interface';
1✔
6
import Time from './Time';
1✔
7
import type {fullDayEventsWeek} from '../typings/interfaces/full-day-events-week.type';
1✔
8
import type {dayInterface} from '../typings/interfaces/day.interface';
1✔
9

1✔
10
interface eventWithJSDatesInterface extends eventInterface {
1✔
11
  timeJS: {
1✔
12
    start: Date,
1✔
13
    end: Date,
1✔
14
  }
1✔
15
}
1✔
16

1✔
17
export default class EventPosition extends Time {
1✔
18
  /**
1✔
19
   * Yields a full calendar week, with all full-day events positioned in it
1✔
20
   * */
1✔
21
  positionFullDayEventsInWeek(weekStart: Date, weekEnd: Date, events: eventInterface[]) {
1✔
22
    // 1. add timeJS.start and timeJS.end to all objects
7✔
23
    const eventsWithJSDates: eventWithJSDatesInterface[] = events.map((scheduleEvent: eventInterface) => {
7✔
24
      const { year: startYear, month: startMonth, date: startDate } = this.getAllVariablesFromDateTimeString(scheduleEvent.time.start)
18✔
25
      const { year: endYear, month: endMonth, date: endDate } = this.getAllVariablesFromDateTimeString(scheduleEvent.time.end)
18✔
26
      scheduleEvent.timeJS = {
18✔
27
        start: new Date(startYear, startMonth, startDate),
18✔
28
        end: new Date(endYear, endMonth, endDate),
18✔
29
      }
18✔
30

18✔
31
      return scheduleEvent as eventWithJSDatesInterface
18✔
32
    }).sort((a, b) => {
7✔
33
      if (a.time.start < b.time.start) return -1;
11!
34
      if (a.time.start > b.time.start) return 1;
11✔
35

3✔
36
      return 0
3✔
37
    })
7✔
38

7✔
39
    // 2. create a week array, where each day is represented as an object with different levels, level1, level2, level3, level4 etc.
7✔
40
    // An event starts on a certain level, the first day when it occurs, and then blocks that level for the rest of its duration
7✔
41
    const allDatesOfWeek = this.getDatesBetweenTwoDates(weekStart, weekEnd)
7✔
42
    const week: fullDayEventsWeek = allDatesOfWeek.map(d => ({ date: d }))
7✔
43

7✔
44
    for (const scheduleEvent of eventsWithJSDates) {
7✔
45
      for (const [dayIndex, day] of week.entries()) {
18✔
46
        const thisDayDateString = this.getDateStringFromDate(day.date)
57✔
47

57✔
48
        if (
57✔
49
          this.getDateStringFromDate(scheduleEvent.timeJS.start) <= thisDayDateString
57✔
50
          && this.getDateStringFromDate(scheduleEvent.timeJS.end) >= thisDayDateString
35✔
51
        ) {
57✔
52
          // 2A. Get the first free level of the day
12✔
53
          let levelToStartOn = 1
12✔
54
          while (typeof week[dayIndex][`level${levelToStartOn}`] !== 'undefined') {
12✔
55
            levelToStartOn++
12✔
56
          }
12✔
57

12✔
58
          // 2B. set the event on this day
12✔
59
          let eventNDays = (Math.ceil((scheduleEvent.timeJS.end.getTime() - day.date.getTime()) / this.MS_PER_DAY) + 1) // Get difference in days, plus the first day itself
12✔
60
          const remainingDaysOfWeek = (week.length - dayIndex)
12✔
61
          if (eventNDays > remainingDaysOfWeek) eventNDays = remainingDaysOfWeek
12✔
62

12✔
63
          week[dayIndex][`level${levelToStartOn}`] = {
12✔
64
            ...scheduleEvent,
12✔
65
            nDays: eventNDays, // Denotes the number of days to display in the week, not the actual number of days
12✔
66
          }
12✔
67

12✔
68
          // 2C. And block the specified level, for the following days of the event
12✔
69
          for (let i = 1; i < eventNDays; i++) {
12✔
70
            week[dayIndex + i][`level${levelToStartOn}`] = 'blocked'
27✔
71
          }
27✔
72

12✔
73
          break
12✔
74
        }
12✔
75
      }
57✔
76
    }
18✔
77

7✔
78
    const weekWithSortedLevelsInDays = []
7✔
79

7✔
80
    // 3. Sort the levels of the day objects
7✔
81
    for (const day of week) {
7✔
82
      weekWithSortedLevelsInDays.push(Object.keys(day).sort().reduce(
25✔
83
        (obj: any, key: any) => {
25✔
84
          obj[key] = day[key];
64✔
85
          return obj;
64✔
86
        },
64✔
87
        {}
25✔
88
      ))
25✔
89
    }
25✔
90

7✔
91
    return weekWithSortedLevelsInDays
7✔
92
  }
7✔
93

1✔
94
  positionFullDayEventsInMonth(
1✔
95
    calendarMonth: dayInterface[][],
32✔
96
    fullDayEvents: eventInterface[]
32✔
97
  ): dayInterface[][] {
32✔
98
    const newMonth: dayInterface[][] = []
32✔
99
    const flatMonth = calendarMonth.flat()
32✔
100
    // Create a map, where each key is a dateString in the format of YYYY-MM-DD, and the value is the calendarDay. This will help us skip 2 levels of nested loops.
32✔
101
    // Instead of iterating over the following units for => week of calendarMonth => day of week => event of fullDayEvents => date of allDatesOfEvent
32✔
102
    // we can stay on 2 levels of nesting with for => fullDayEvent of fullDayEvents => date of allDatesOfEvent, and then see if the map has a matching date
32✔
103
    const monthMap = new Map()
32✔
104
    flatMonth.forEach(day => monthMap.set(this.dateStringFrom(day.dateTimeString), day))
32✔
105
    // Sort events with the latest first. This will help the algorithm place the oldest events at the start of each "events" array later
32✔
106
    fullDayEvents = fullDayEvents.sort((a, b) => {
32✔
107
      if (a.time.start < b.time.start) return 1;
2!
108
      if (a.time.start > b.time.start) return -1;
2!
109

×
110
      return 0
×
111
    })
32✔
112

32✔
113
    for (const fullDayEvent of fullDayEvents) {
32✔
114
      const { year: startYear, month: startMonth, date: startDate } = this.getAllVariablesFromDateTimeString(fullDayEvent.time.start)
4✔
115
      const { year: endYear, month: endMonth, date: endDate } = this.getAllVariablesFromDateTimeString(fullDayEvent.time.end)
4✔
116
      const allDatesOfEvent = this.getDatesBetweenTwoDates(
4✔
117
        new Date(startYear, startMonth, startDate),
4✔
118
        new Date(endYear, endMonth, endDate),
4✔
119
      )
4✔
120

4✔
121
      for (const date of allDatesOfEvent) {
4✔
122
        const dateString = this.getDateStringFromDate(date)
39✔
123
        const dateInMap = monthMap.get(dateString)
39✔
124

39✔
125
        if (dateInMap) monthMap.set(dateString, {
39✔
126
          ...dateInMap,
39✔
127
          // Since we're iterating over the fullDayEvents sorted backwards, the earliest events will end up first (which is the wanted behavior)
39✔
128
          events: [fullDayEvent, ...dateInMap.events]
39✔
129
        })
39✔
130
      }
39✔
131
    }
4✔
132

32✔
133
    let weekIterator = 0
32✔
134

32✔
135
    monthMap.forEach(day => {
32✔
136
      // For the very first day, create an array for the first week, and push day onto it
1,127✔
137
      if ( ! newMonth.length) newMonth.push([day])
1,127✔
138

1,095✔
139
      // When the week exists, and is not yet filled with 7 days, push day onto week
1,095✔
140
      else if (newMonth[weekIterator] && newMonth[weekIterator].length < 7) newMonth[weekIterator].push(day)
1,095✔
141

129✔
142
      // When the week exists, but is full, create a new week with the day in it, and increment the week iterator
129✔
143
      else if (newMonth[weekIterator] && newMonth[weekIterator].length === 7) {
129✔
144
        newMonth.push([day])
129✔
145
        weekIterator++
129✔
146
      }
129✔
147
    })
32✔
148

32✔
149
    return newMonth
32✔
150
  }
32✔
151
}
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc