-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
365 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,186 @@ | ||
import { getStartEndWeek, getWeekCourses } from '../src/helpers/calendar'; | ||
import dayjs from '../src/lib/dayjs'; | ||
import type { DetailedEnrolledCourse, WeekCourses } from '../src/types/course'; | ||
|
||
describe('getStartEndWeek', () => { | ||
it('should return the first date (Monday) of the start and end week', () => { | ||
const [start, end] = getStartEndWeek([ | ||
{ start: '09-18', end: '10-12' }, | ||
{ start: '11-13', end: '12-01' }, | ||
{ start: '12-12', end: '11-11' }, | ||
]); | ||
expect(start.format('MM-DD')).toBe('09-16'); | ||
expect(end.format('MM-DD')).toBe('11-25'); | ||
}); | ||
}); | ||
|
||
describe('getWeekCourses', () => { | ||
it('should return the courses for each day of the week', () => { | ||
const enrolledCourses: Array<DetailedEnrolledCourse> = [ | ||
{ | ||
id: 'm', | ||
name: { code: 'm', subject: 'm', title: 'math' }, | ||
classes: [ | ||
{ | ||
type: 'Lecture', | ||
id: 'l', | ||
meetings: [ | ||
{ | ||
location: 'bragg', | ||
day: 'Tuesday', | ||
date: { start: '09-09', end: '09-27' }, | ||
time: { start: '09:00', end: '10:00' }, | ||
}, | ||
], | ||
}, | ||
], | ||
}, | ||
{ | ||
id: 'cs', | ||
name: { code: 'cs', subject: 'cs', title: 'compsci' }, | ||
classes: [ | ||
{ | ||
type: 'Practical', | ||
id: 'p', | ||
meetings: [ | ||
{ | ||
location: 'online', | ||
day: 'Monday', | ||
date: { start: '09-09', end: '09-27' }, | ||
time: { start: '17:00', end: '18:00' }, | ||
}, | ||
], | ||
}, | ||
{ | ||
type: 'Workshop', | ||
id: 'w', | ||
meetings: [ | ||
{ | ||
location: 'iw', | ||
day: 'Friday', | ||
date: { start: '09-09', end: '09-27' }, | ||
time: { start: '09:00', end: '10:00' }, | ||
}, | ||
], | ||
}, | ||
], | ||
}, | ||
]; | ||
const courses = getWeekCourses(dayjs('2024-09-16'), enrolledCourses); | ||
const expectedRes: WeekCourses = { | ||
Monday: [ | ||
{ | ||
id: 'cs', | ||
name: { code: 'cs', subject: 'cs', title: 'compsci' }, | ||
classId: 'p', | ||
classType: 'Practical', | ||
location: 'online', | ||
time: { start: '17:00', end: '18:00' }, | ||
}, | ||
], | ||
Tuesday: [ | ||
{ | ||
id: 'm', | ||
name: { code: 'm', subject: 'm', title: 'math' }, | ||
classId: 'l', | ||
classType: 'Lecture', | ||
location: 'bragg', | ||
time: { start: '09:00', end: '10:00' }, | ||
}, | ||
], | ||
Wednesday: [], | ||
Thursday: [], | ||
Friday: [ | ||
{ | ||
id: 'cs', | ||
name: { code: 'cs', subject: 'cs', title: 'compsci' }, | ||
classId: 'w', | ||
classType: 'Workshop', | ||
location: 'iw', | ||
time: { start: '09:00', end: '10:00' }, | ||
}, | ||
], | ||
}; | ||
expect(courses).toEqual(expectedRes); | ||
}); | ||
it('should return the courses if course start at the end of the week', () => { | ||
const enrolledCourses: Array<DetailedEnrolledCourse> = [ | ||
{ | ||
id: 'm', | ||
name: { code: 'm', subject: 'm', title: 'math' }, | ||
classes: [ | ||
{ | ||
type: 'Lecture', | ||
id: 'l', | ||
meetings: [ | ||
{ | ||
location: 'bragg', | ||
day: 'Friday', | ||
date: { start: '09-20', end: '10-04' }, | ||
time: { start: '09:00', end: '10:00' }, | ||
}, | ||
], | ||
}, | ||
], | ||
}, | ||
]; | ||
const courses = getWeekCourses(dayjs('2024-09-16'), enrolledCourses); | ||
const expectedRes: WeekCourses = { | ||
Monday: [], | ||
Tuesday: [], | ||
Wednesday: [], | ||
Thursday: [], | ||
Friday: [ | ||
{ | ||
id: 'm', | ||
name: { code: 'm', subject: 'm', title: 'math' }, | ||
classId: 'l', | ||
classType: 'Lecture', | ||
location: 'bragg', | ||
time: { start: '09:00', end: '10:00' }, | ||
}, | ||
], | ||
}; | ||
expect(courses).toEqual(expectedRes); | ||
}); | ||
it('should return the courses if course end at the start of the week', () => { | ||
const enrolledCourses: Array<DetailedEnrolledCourse> = [ | ||
{ | ||
id: 'm', | ||
name: { code: 'm', subject: 'm', title: 'math' }, | ||
classes: [ | ||
{ | ||
type: 'Lecture', | ||
id: 'l', | ||
meetings: [ | ||
{ | ||
location: 'bragg', | ||
day: 'Monday', | ||
date: { start: '08-12', end: '09-16' }, | ||
time: { start: '09:00', end: '10:00' }, | ||
}, | ||
], | ||
}, | ||
], | ||
}, | ||
]; | ||
const courses = getWeekCourses(dayjs('2024-09-16'), enrolledCourses); | ||
const expectedRes: WeekCourses = { | ||
Monday: [ | ||
{ | ||
id: 'm', | ||
name: { code: 'm', subject: 'm', title: 'math' }, | ||
classId: 'l', | ||
classType: 'Lecture', | ||
location: 'bragg', | ||
time: { start: '09:00', end: '10:00' }, | ||
}, | ||
], | ||
Tuesday: [], | ||
Wednesday: [], | ||
Thursday: [], | ||
Friday: [], | ||
}; | ||
expect(courses).toEqual(expectedRes); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import { Button } from '@nextui-org/react'; | ||
import { useEffect } from 'react'; | ||
|
||
import { useCalendar } from '../helpers/calendar'; | ||
|
||
export const Calendar = () => { | ||
const { courses, currentWeek, nextWeek, prevWeek } = useCalendar(); | ||
useEffect(() => { | ||
console.log(courses); | ||
}); | ||
|
||
return ( | ||
<div> | ||
<h1>{currentWeek.format('MMMM D, YYYY')}</h1> | ||
<Button | ||
isIconOnly | ||
variant="light" | ||
className="text-2xl" | ||
onClick={prevWeek} | ||
> | ||
⬅️ | ||
</Button> | ||
<Button | ||
isIconOnly | ||
variant="light" | ||
className="text-2xl" | ||
onClick={nextWeek} | ||
> | ||
➡️ | ||
</Button> | ||
</div> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
import { useEffect, useState } from 'react'; | ||
|
||
import { useDetailedEnrolledCourses } from '../data/enrolled-courses'; | ||
import dayjs from '../lib/dayjs'; | ||
import type { | ||
DetailedEnrolledCourse, | ||
WeekCourse, | ||
WeekCourses, | ||
} from '../types/course'; | ||
import { dateToDayjs, getMonday } from '../utils/date'; | ||
|
||
const MAX_DATE = dayjs('6666-06-06'); | ||
const MIN_DATE = dayjs('2005-03-12'); | ||
|
||
/** | ||
* Get the start date (Monday) of the start and end week | ||
* @param dates Dates for all enrolled meetings | ||
* @returns Tuple of dates of the start and end week | ||
*/ | ||
export const getStartEndWeek = ( | ||
dates: Array<{ start: string; end: string }>, | ||
): [dayjs.Dayjs, dayjs.Dayjs] => { | ||
const currentMonday = getMonday(dayjs()); | ||
if (dates.length === 0) return [currentMonday, currentMonday]; | ||
|
||
let startWeek = MAX_DATE; | ||
let endWeek = MIN_DATE; | ||
dates.forEach((date) => { | ||
const start = dateToDayjs(date.start); | ||
const end = dateToDayjs(date.end); | ||
if (start.isBefore(startWeek)) { | ||
startWeek = start; | ||
} | ||
if (end.isAfter(endWeek)) { | ||
endWeek = end; | ||
} | ||
}); | ||
|
||
return [getMonday(startWeek), getMonday(endWeek)]; | ||
}; | ||
|
||
/** | ||
* Get courses for each day of the week | ||
* @param weekStart Start of the week (Monday) | ||
* @param enrolledCourses All detailed enrolled courses | ||
* @returns Object with courses for each day of the week | ||
*/ | ||
export const getWeekCourses = ( | ||
weekStart: dayjs.Dayjs, | ||
enrolledCourses: Array<DetailedEnrolledCourse>, | ||
): WeekCourses => { | ||
const weekEnd = weekStart.add(4, 'days'); | ||
const courses: WeekCourses = { | ||
Monday: [], | ||
Tuesday: [], | ||
Wednesday: [], | ||
Thursday: [], | ||
Friday: [], | ||
}; | ||
|
||
enrolledCourses.forEach((c) => { | ||
c.classes.forEach((cl) => { | ||
cl.meetings.forEach((m) => { | ||
const isMeetingInWeek = | ||
weekEnd.isSameOrAfter(dateToDayjs(m.date.start)) && | ||
weekStart.isSameOrBefore(dateToDayjs(m.date.end)); | ||
if (!isMeetingInWeek) return; | ||
const course: WeekCourse = { | ||
id: c.id, | ||
name: c.name, | ||
classId: cl.id, | ||
classType: cl.type, | ||
location: m.location, | ||
time: m.time, | ||
}; | ||
courses[m.day].push(course); | ||
}); | ||
}); | ||
}); | ||
|
||
return courses; | ||
}; | ||
|
||
export const useCalendar = () => { | ||
const enrolledCourses = useDetailedEnrolledCourses(); | ||
|
||
const dates = enrolledCourses.flatMap((c) => | ||
c.classes.flatMap((cl) => cl.meetings.flatMap((m) => m.date)), | ||
); | ||
const [startWeek, endWeek] = getStartEndWeek(dates); | ||
|
||
const [currentWeek, setCurrentWeek] = useState(getMonday(dayjs())); | ||
|
||
useEffect(() => { | ||
if (currentWeek.isBefore(startWeek)) { | ||
setCurrentWeek(startWeek); | ||
} | ||
if (currentWeek.isAfter(endWeek)) { | ||
setCurrentWeek(endWeek); | ||
} | ||
}, [startWeek, endWeek, currentWeek]); | ||
|
||
const nextWeek = () => { | ||
if (currentWeek.isSame(endWeek)) return; | ||
setCurrentWeek((c) => c.add(1, 'week')); | ||
}; | ||
const prevWeek = () => { | ||
if (currentWeek.isSame(startWeek)) return; | ||
setCurrentWeek((c) => c.subtract(1, 'week')); | ||
}; | ||
|
||
const courses = getWeekCourses(currentWeek, enrolledCourses); | ||
|
||
return { currentWeek, nextWeek, prevWeek, courses }; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,12 @@ | ||
import dayjs from 'dayjs'; | ||
import customParseFormat from 'dayjs/plugin/customParseFormat'; | ||
import isSameOrAfter from 'dayjs/plugin/isSameOrAfter'; | ||
import isSameOrBefore from 'dayjs/plugin/isSameOrBefore'; | ||
import isoWeek from 'dayjs/plugin/isoWeek'; | ||
|
||
dayjs.extend(customParseFormat); | ||
dayjs.extend(isoWeek); | ||
dayjs.extend(isSameOrBefore); | ||
dayjs.extend(isSameOrAfter); | ||
|
||
export default dayjs; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.