Skip to content

Commit 21cfba4

Browse files
committed
Merge branch 'main' into release/1.5
2 parents 50af809 + 21f2214 commit 21cfba4

File tree

3 files changed

+197
-2
lines changed

3 files changed

+197
-2
lines changed

course-matrix/backend/src/controllers/aiController.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,6 @@ export const chat = asyncHandler(async (req: Request, res: Response) => {
244244
- Allowing natural language queries about courses, offerings, and academic programs
245245
- Providing personalized recommendations based on degree requirements and course availability
246246
- Creating, reading, updating, and deleting user timetables based on natural language
247-
248247
## Your Capabilities
249248
- Create new timetables based on provided courses and restrictions
250249
- Update timetable names and semesters

course-matrix/backend/src/controllers/timetablesController.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,6 @@ export default {
6767
.json({ error: "A timetable with this title already exists" });
6868
}
6969
//Create query to insert the user_id and timetable_title into the db
70-
7170
let insertTimetable = supabase
7271
.schema("timetable")
7372
.from("timetables")
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
import { ScheduleXCalendar } from "@schedule-x/react";
2+
import {
3+
createCalendar,
4+
createViewDay,
5+
createViewMonthAgenda,
6+
createViewMonthGrid,
7+
createViewWeek,
8+
viewWeek,
9+
} from "@schedule-x/calendar";
10+
// import { createDragAndDropPlugin } from "@schedule-x/drag-and-drop";
11+
import { createEventModalPlugin } from "@schedule-x/event-modal";
12+
import "@schedule-x/theme-default/dist/index.css";
13+
import React from "react";
14+
import { useGetSharedEventsQuery } from "@/api/eventsApiSlice";
15+
import { Event, TimetableEvents, Restriction } from "@/utils/type-utils";
16+
import {
17+
getSemesterStartAndEndDates,
18+
getSemesterStartAndEndDatesPlusOneWeek,
19+
} from "@/utils/semester-utils";
20+
import { courseEventStyles } from "@/constants/calendarConstants";
21+
import { parseEvent } from "@/utils/calendar-utils";
22+
import { useGetSharedRestrictionsQuery } from "@/api/sharedApiSlice";
23+
import { formatTime } from "@/utils/format-date-time";
24+
import LoadingPage from "../Loading/LoadingPage";
25+
26+
interface SharedCalendarProps {
27+
user_id: string;
28+
user_username: string;
29+
calendar_id: number;
30+
timetable_title: string;
31+
semester: string;
32+
}
33+
34+
const SharedCalendar = React.memo<SharedCalendarProps>(
35+
({ user_id, user_username, calendar_id, timetable_title, semester }) => {
36+
const semesterStartDate = getSemesterStartAndEndDates(semester).start;
37+
const { start: semesterStartDatePlusOneWeek, end: semesterEndDate } =
38+
getSemesterStartAndEndDatesPlusOneWeek(semester);
39+
40+
const { data: sharedEventsData, isLoading: isSharedEventsLoading } =
41+
useGetSharedEventsQuery(
42+
{ user_id, calendar_id },
43+
{ skip: !user_id || !calendar_id },
44+
) as {
45+
data: TimetableEvents;
46+
isLoading: boolean;
47+
};
48+
const sharedEvents = sharedEventsData as TimetableEvents;
49+
50+
const { data: restrictionsData, isLoading: isRestrictionsLoading } =
51+
useGetSharedRestrictionsQuery(
52+
{ user_id, calendar_id },
53+
{ skip: !user_id || !calendar_id },
54+
) as {
55+
data: Restriction[];
56+
isLoading: boolean;
57+
};
58+
const restrictions = restrictionsData ?? [];
59+
60+
const isLoading = isSharedEventsLoading || isRestrictionsLoading;
61+
62+
const courseEvents: Event[] = sharedEvents?.courseEvents ?? [];
63+
const userEvents: Event[] = sharedEvents?.userEvents ?? [];
64+
65+
const courses = [
66+
...new Set(
67+
courseEvents.map((event) => event.event_name.split("-")[0].trim()),
68+
),
69+
];
70+
const courseToMeetingSectionMap = new Map<string, string[]>();
71+
courseEvents.forEach((event) => {
72+
const course = event.event_name.split("-")[0].trim();
73+
const meetingSection = event.event_name.split("-")[1].trim();
74+
if (courseToMeetingSectionMap.has(course)) {
75+
const meetingSections = courseToMeetingSectionMap.get(course);
76+
if (meetingSections) {
77+
courseToMeetingSectionMap.set(course, [
78+
...new Set([...meetingSections, meetingSection]),
79+
]);
80+
}
81+
} else {
82+
courseToMeetingSectionMap.set(course, [meetingSection]);
83+
}
84+
});
85+
86+
let index = 1;
87+
const courseEventsParsed = courseEvents.map((event) =>
88+
parseEvent(index++, event, "courseEvent"),
89+
);
90+
const userEventsParsed = userEvents.map((event) =>
91+
parseEvent(index++, event, "userEvent"),
92+
);
93+
94+
const calendar = createCalendar({
95+
views: [
96+
createViewDay(),
97+
createViewWeek(),
98+
createViewMonthGrid(),
99+
createViewMonthAgenda(),
100+
],
101+
firstDayOfWeek: 0,
102+
selectedDate: semesterStartDatePlusOneWeek,
103+
minDate: semesterStartDate,
104+
maxDate: semesterEndDate,
105+
defaultView: viewWeek.name,
106+
events: [...courseEventsParsed, ...userEventsParsed],
107+
calendars: {
108+
courseEvent: courseEventStyles,
109+
},
110+
plugins: [createEventModalPlugin()],
111+
weekOptions: {
112+
gridHeight: 600,
113+
},
114+
dayBoundaries: {
115+
start: "06:00",
116+
end: "21:00",
117+
},
118+
isResponsive: false,
119+
});
120+
121+
const username =
122+
user_username.trim().length > 0 ? user_username : "John Doe";
123+
124+
return isLoading ? (
125+
<LoadingPage />
126+
) : (
127+
<div>
128+
<h1 className="text-xl text-center justify-between font-medium tracking-tight mb-4">
129+
You are viewing{" "}
130+
<span className="text-green-500">{username ?? "John Doe"}'s</span>{" "}
131+
timetable named{" "}
132+
<span className="text-green-500">{timetable_title}</span> for{" "}
133+
<span className="text-green-500">{semester}</span>
134+
</h1>
135+
<div className="text-sm justify-between tracking-tight mb-2">
136+
<b>Courses:</b>{" "}
137+
{courses.length === 0 && (
138+
<span className="text-sm text-red-500">
139+
This timetable has no courses
140+
</span>
141+
)}
142+
{courses.map((course) => (
143+
<span className="text-blue-500 mr-2" key={course}>
144+
{course}{" "}
145+
<span className="text-yellow-500">
146+
({(courseToMeetingSectionMap.get(course) ?? []).join(", ")})
147+
</span>
148+
</span>
149+
))}
150+
</div>
151+
<b className="text-sm">Restrictions: </b>
152+
{restrictions.length === 0 && (
153+
<span className="text-sm text-red-500">No restrictions applied</span>
154+
)}
155+
<div className="grid grid-rows-3 text-sm justify-between mb-4">
156+
{restrictions.map((restriction) => {
157+
const restrictedDays = JSON.parse(restriction.days)
158+
.map((day: string) => {
159+
if (day === "MO") return "Monday";
160+
if (day === "TU") return "Tuesday";
161+
if (day === "WE") return "Wednesday";
162+
if (day === "TH") return "Thursday";
163+
if (day === "FR") return "Friday";
164+
if (day === "SA") return "Saturday";
165+
if (day === "SU") return "Sunday";
166+
return "Unknown Day";
167+
})
168+
.join(", ");
169+
const restrictionText =
170+
restriction.type === "Max Gap"
171+
? `${restriction.type} of ${restriction.max_gap} Hours on ${restriction.days}`
172+
: restriction.type === "Restrict Before"
173+
? `${restriction.type} ${formatTime(new Date(`2025-01-01T${restriction.end_time}.00Z`))} on ${restrictedDays}`
174+
: restriction.type === "Restrict After"
175+
? `${restriction.type} ${formatTime(new Date(`2025-01-01T${restriction.start_time}.00Z`))} on ${restrictedDays}`
176+
: restriction.type === "Restrict Between"
177+
? `${restriction.type} ${formatTime(new Date(`2025-01-01T${restriction.start_time}.00Z`))} and ${formatTime(new Date(`2025-01-01T${restriction.end_time}.00Z`))} on ${restrictedDays}`
178+
: restriction.type === "Restrict Day"
179+
? `Restrict the days of ${restrictedDays}`
180+
: restriction.type === "Days Off"
181+
? `Minimum of ${restriction.num_days} days off`
182+
: "Unknown Restriction Applied";
183+
184+
return (
185+
<div className="text-red-500" key={restriction.id}>
186+
{restrictionText}
187+
</div>
188+
);
189+
})}
190+
</div>
191+
<ScheduleXCalendar calendarApp={calendar} />
192+
</div>
193+
);
194+
},
195+
);
196+
197+
export default SharedCalendar;

0 commit comments

Comments
 (0)