Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
89 commits
Select commit Hold shift + click to select a range
211d03b
Add ux ui mockups link (#4)
kevin-lann Jan 28, 2025
c880b5d
Add Product.md file (#5) - Scrum 19
minhhaitran08 Jan 30, 2025
68ba14f
Finish all sections of README.md apart from the Installation section …
Austin-X Jan 30, 2025
40cb488
Ty/scrum 63 added product_backlog.md (#11)
thomasyzy7 Jan 31, 2025
61030d1
Kl/scrum 21 installation (#10)
kevin-lann Jan 31, 2025
18ac6de
Kl/scrum 20 project setup (#12)
kevin-lann Jan 31, 2025
9230bbf
Ms/scrum 23 added personas (#9)
MasahisaSekita Jan 31, 2025
eaf04ba
Fix: README .env typo (#13)
kevin-lann Jan 31, 2025
ab8a7e1
Release/0.1 - Develop (#15)
thomasyzy7 Feb 1, 2025
ccf59f9
[Hotfix]: Add missing section in README.md for "Tech Stack and Softwa…
Austin-X Feb 1, 2025
e27c8ee
Kl/scrum 69 extract info (#18)
kevin-lann Feb 9, 2025
a90b064
Setup redux store and api slice (#19)
kevin-lann Feb 10, 2025
e59147e
Kl/scrum 71 course list frontend (#21)
kevin-lann Feb 11, 2025
5d9786d
Set up /api/courses and /api/departments endpoints in backend (#22)
Austin-X Feb 12, 2025
ac83923
Ms/scrum 26 login and signup (#25)
MasahisaSekita Feb 13, 2025
e0338e7
Integrate Course List and Entries display (#23)
kevin-lann Feb 13, 2025
b94deb2
Ty/scrum 80 user auth backend (#26)
thomasyzy7 Feb 14, 2025
16c3993
Add User Menu Dropdown Component (#27)
Austin-X Feb 14, 2025
a2d17a2
Integrate Login and Signup (#28)
kevin-lann Feb 14, 2025
bf85a8f
Account Logout Functionality (#29)
Austin-X Feb 14, 2025
66229ba
SCRUM70 - Mt/scrum 70 sprint1 documentation (#20)
minhhaitran08 Feb 15, 2025
d859df0
Release/1.0 - Develop (#31)
thomasyzy7 Feb 15, 2025
0c61165
[SCRUM-107]: Set up Github Workflow to auto-format unformatted code u…
Austin-X Feb 18, 2025
0209f3e
Ty/scrum 95 Reset Password Backend (#32)
thomasyzy7 Feb 18, 2025
0dc08b2
Kl/scrum 110 document frontend (#35)
kevin-lann Feb 19, 2025
3e47072
Ax/scrum 111 document backend (#36)
Austin-X Feb 19, 2025
6c36d1c
Kl/scrum 112 Bugfix add password max length validation (#40)
kevin-lann Feb 19, 2025
76339d3
Kl/scrum 113 Set user session after login to handle RLS & Modify back…
kevin-lann Feb 20, 2025
64f971a
Kl/scrum 114 fix redirect login (#41)
kevin-lann Feb 20, 2025
b67705d
Kl/scrum 117 fix auth middleware (#44)
kevin-lann Feb 21, 2025
2830739
Kl/scrum 115 setup assistant UI (#45)
kevin-lann Feb 28, 2025
69e3275
Ty/scrum 119 sprint2 iteration (#47)
thomasyzy7 Mar 4, 2025
218c1bb
Ms/scrum 95 account edit (#43)
MasahisaSekita Mar 4, 2025
de664e4
Mt/scrum 46 child 102 create user timetable backend endpoints (#46)
minhhaitran08 Mar 5, 2025
88a1b55
Kl/scrum 116 chatbot rag (#48)
kevin-lann Mar 6, 2025
79f091b
kl/scrum-121 Fix cors line in index.ts (#57)
kevin-lann Mar 7, 2025
46f0384
Kl/scrum 36 chats (#51)
kevin-lann Mar 7, 2025
6f15fb9
Ax/scrum 123 Set up Unit Testing Template and Testing Workflow (#58)
Austin-X Mar 8, 2025
f57f4de
Ax/scrum 101 Finish Timetable Home Page (#50)
Austin-X Mar 8, 2025
5a910a8
Ms/scrum 124 sprint2 retrospective (#59)
thomasyzy7 Mar 8, 2025
3f1c6aa
Release/1.2 - Develop (#61)
thomasyzy7 Mar 8, 2025
8de4414
Ax/scrum 125 Fix Module Import Linting Errors (#63)
Austin-X Mar 9, 2025
a982d8e
Update README.md (#62)
MasahisaSekita Mar 13, 2025
2c6d152
Kl/scrum-132 Improved querying of year level and breadth requirement …
kevin-lann Mar 15, 2025
8479c79
kl/SCRUM-129: Fix restriction and form related bugs (#67)
kevin-lann Mar 16, 2025
3d814b0
Mt/scrum-57-child-133-update-timetable-endpoints-for-favorites (#68)
minhhaitran08 Mar 16, 2025
ea8454e
Mt/scrum 137 check timetable duplicated name (#70)
minhhaitran08 Mar 16, 2025
e71811e
Mt/scrum 58 child 60 sharing timetable backend (#73)
minhhaitran08 Mar 18, 2025
e34ce55
Ty/scrum 52 timetable generation (#74)
thomasyzy7 Mar 20, 2025
8bba298
Hotfix 1.0.3 - Revert "Ty/scrum 52 timetable generation (#74)" (#80)
kevin-lann Mar 20, 2025
c12fbd0
Ty/scrum 52 timetable generation (#81)
thomasyzy7 Mar 20, 2025
ad6c9c3
Kl/scrum 56 email notifications (#78)
kevin-lann Mar 20, 2025
f8adc57
Ms/scrum 127 update sprint 3 documentation (#72)
MasahisaSekita Mar 20, 2025
e93d766
Ax/scrum 122 Timetable Basics Integration (#69)
Austin-X Mar 21, 2025
9c358fc
Ax/scrum 144 Add integration tests (#83)
Austin-X Mar 21, 2025
fbb2a44
Kl/scrum 41 ai timetable generate (#75)
kevin-lann Mar 21, 2025
4bb785f
Kl/scrum 55 timetable generation integration (#85)
kevin-lann Mar 21, 2025
202c807
Mt/scrum 138 child 139 backend testing timetablesController, restrict…
minhhaitran08 Mar 21, 2025
32ef75d
Ty/scrum 128 sprint restrospective (#89)
thomasyzy7 Mar 22, 2025
5570acf
Release/1.3 [develop] (#92)
kevin-lann Mar 22, 2025
e3ebef5
Hotfix 1.0.4 [develop] (#93)
kevin-lann Mar 22, 2025
234c1a4
Ms/scrum 130 project deployment CI/CD (#79)
MasahisaSekita Mar 23, 2025
13cc5f8
Kl/scrum 154 fix time strings (#101)
kevin-lann Mar 23, 2025
33ee4a7
Ax/scrum 146 fix same-name-timetable frontend bug (#100)
Austin-X Mar 23, 2025
bebf8c0
Ty/scrum 153 timetable generation bug (#104)
thomasyzy7 Mar 23, 2025
d00d315
Ms/scrum 130 project deployment with latest documentation (#105)
MasahisaSekita Mar 23, 2025
96655f1
Kl/scrum 148 chatbot fixes (#102)
kevin-lann Mar 24, 2025
440f93a
Ty/scrum 57 timetable favourite (#106)
thomasyzy7 Mar 24, 2025
5f2e0bb
Ty/scrum 155 generator refinement (#107)
thomasyzy7 Mar 25, 2025
198f5f2
Kl/scrum 147 fix timetable integration bugs (#103)
kevin-lann Mar 25, 2025
a166876
Ty/scrum 156 + 157 (#112)
thomasyzy7 Mar 25, 2025
9ad7166
Kl/scrum 62 timetable compare (#108)
kevin-lann Mar 26, 2025
69db0ce
Kl/scrum 158 ai hallucinating (#115)
kevin-lann Mar 26, 2025
5d7e766
CI/CD Pipeline with assignment 2 documentation (#120)
MasahisaSekita Mar 27, 2025
7550a07
Ty/scrum 161 favourite bug (#119)
thomasyzy7 Mar 27, 2025
265aba0
Ax/scrum 60 Timetable Sharing Feature (#117)
Austin-X Mar 27, 2025
1230a30
Release/1.4 - develop (#127)
thomasyzy7 Mar 27, 2025
304912d
Kl/scrum 166 restrictions hide (#122)
kevin-lann Mar 27, 2025
289998c
ms/Scrum-170-Clean-UserMenu-so-there-are-no-useless-fields (#131)
MasahisaSekita Mar 27, 2025
7393306
Ax/scrum 167 Integrate Shared Timetables into Timetable Compare Featu…
Austin-X Mar 28, 2025
dcc4eda
Kl/scrum 169 select all (#135)
kevin-lann Mar 28, 2025
ed4a46b
Ax/scrum 131 Fix Text Highlight Bug for Edit Username Flow (#133)
Austin-X Mar 28, 2025
97047f9
Ax/scrum 145 timetable frontend enhancements (#134)
Austin-X Mar 28, 2025
2e2a45d
Ty/scrum 159 timetable limit (#130)
kevin-lann Mar 29, 2025
25538ae
Ty/scrum 162 last edited refresh (#129)
thomasyzy7 Mar 29, 2025
7d65b17
Kl/scrum 171 misc frontend generation bugs (#136)
kevin-lann Mar 29, 2025
50af809
Kl/scrum 168 image + Ui/ux enhancements (#137)
kevin-lann Mar 30, 2025
21cfba4
Merge branch 'main' into release/1.5
Austin-X Mar 30, 2025
fb0c60f
Remove the duplicated format workflow
Austin-X Mar 30, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 0 additions & 22 deletions .github/workflows/workflow.yml

This file was deleted.

23 changes: 1 addition & 22 deletions course-matrix/backend/__tests__/timetablesController.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -320,27 +320,6 @@ describe("PUT /api/timetables/:id", () => {
beforeEach(() => {
jest.clearAllMocks();
});
test("should return error code 400 and message 'New timetable title or semester or updated favorite status is required when updating a timetable' if request body is empty", async () => {
// Make sure the test user is authenticated
const user_id = "testuser04-f84fd0da-d775-4424-ad88-d9675282453c";
const timetableData = {};

// Mock authHandler to simulate the user being logged in
(
authHandler as jest.MockedFunction<typeof authHandler>
).mockImplementationOnce(mockAuthHandler(user_id));

const response = await request(app)
.put("/api/timetables/1")
.send(timetableData);

// Check that the `update` method was called
expect(response.statusCode).toBe(400);
expect(response.body).toEqual({
error:
"New timetable title or semester or updated favorite status or email notifications enabled is required when updating a timetable",
});
});

test("should update the timetable successfully", async () => {
// Make sure the test user is authenticated
Expand All @@ -361,7 +340,7 @@ describe("PUT /api/timetables/:id", () => {

// Check that the `update` method was called
expect(response.statusCode).toBe(200);
expect(response.body).toEqual({
expect(response.body).toMatchObject({
timetable_title: "Updated Title",
semester: "Spring 2025",
});
Expand Down
31 changes: 26 additions & 5 deletions course-matrix/backend/src/constants/availableFunctions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ export const availableFunctions: AvailableFunctions = {
try {
// Retrieve user_id
const user_id = (req as any).user.id;

// Retrieve user timetable item based on user_id
let timeTableQuery = supabase
.schema("timetable")
Expand All @@ -63,7 +62,11 @@ export const availableFunctions: AvailableFunctions = {
};
}

return { status: 200, data: timetableData };
return {
status: 200,
timetableCount: timetableData.length,
data: timetableData,
};
} catch (error) {
console.log(error);
return { status: 400, error: error };
Expand Down Expand Up @@ -113,6 +116,7 @@ export const availableFunctions: AvailableFunctions = {
}

let updateData: any = {};
updateData.updated_at = new Date().toISOString();
if (timetable_title) updateData.timetable_title = timetable_title;
if (semester) updateData.semester = semester;

Expand Down Expand Up @@ -198,13 +202,33 @@ export const availableFunctions: AvailableFunctions = {
try {
// Extract event details and course information from the request
const { name, semester, courses, restrictions } = args;
// Get user id from session authentication to insert in the user_id col
const user_id = (req as any).user.id;

if (name.length > 50) {
return {
status: 400,
error: "timetable title is over 50 characters long",
};
}

// Timetables cannot exceed the size of 25.
const { count: timetable_count, error: timetableCountError } =
await supabase
.schema("timetable")
.from("timetables")
.select("*", { count: "exact", head: true })
.eq("user_id", user_id);

console.log(timetable_count);

if ((timetable_count ?? 0) >= 25) {
return {
status: 400,
error: "You have exceeded the limit of 25 timetables",
};
}

const courseOfferingsList: OfferingList[] = [];
const validCourseOfferingsList: GroupedOfferingList[] = [];
const maxdays = getMaxDays(restrictions);
Expand Down Expand Up @@ -277,9 +301,6 @@ export const availableFunctions: AvailableFunctions = {

// ------ CREATE FLOW ------

// Get user id from session authentication to insert in the user_id col
const user_id = (req as any).user.id;

// Retrieve timetable title
const schedule = trim(validSchedules)[0];
if (!name || !semester) {
Expand Down
12 changes: 7 additions & 5 deletions course-matrix/backend/src/controllers/aiController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,6 @@ export const chat = asyncHandler(async (req: Request, res: Response) => {
- Allowing natural language queries about courses, offerings, and academic programs
- Providing personalized recommendations based on degree requirements and course availability
- Creating, reading, updating, and deleting user timetables based on natural language

## Your Capabilities
- Create new timetables based on provided courses and restrictions
- Update timetable names and semesters
Expand All @@ -266,11 +265,12 @@ export const chat = asyncHandler(async (req: Request, res: Response) => {
}/dashboard/timetable?edit=[[TIMETABLE_ID]] , where TIMETABLE_ID is the id of the respective timetable.
- If the user provides a course code of length 6 like CSCA08, then assume they mean CSCA08H3 (H3 appended)
- If the user wants to create a timetable:
1. First call getCourses to get course information on the requested courses,
2. If the user provided a semester, then call getOfferings with the provided courses and semester to ensure the courses are actually offered in the semester.
1. First call getTimetables to refetch most recent info on timetable names + timetableCount. DO NOT assume timetable names and # of timetables is same since last query.
2. Then call getCourses to get course information on the requested courses,
3. If the user provided a semester, then call getOfferings with the provided courses and semester to ensure the courses are actually offered in the semester.
a) If a course is NOT returned by getOFferings, then list it under "Excluded courses" with "reason: not offered in [provided semester]"
b) If no courses have offerings, then do not generate the timetable.
3. Lastly, call generateTimetable with the provided information.
4. Lastly, call generateTimetable with the provided information.
- Do not make up fake courses or offerings.
- If a user asks about a course that you do not know of, acknowledge this.
- You can only edit title of the timetable, nothing else. If a user tries to edit something else, acknowledge this limitation.
Expand All @@ -280,12 +280,14 @@ export const chat = asyncHandler(async (req: Request, res: Response) => {
- After a deletion has been cancelled, /timetable confirm will do nothing. If the user wants to delete again after cancelling, they must specify so.
- Do not create multiple timetables for a single user query. Each user query can create at most 1 timetable
- If you try to update or create a timetable but you get an error saying a timetable with the same name already exists, then ask the user to rename
- It is possible for users to delete timetables / update them manually. For this reason, always refetch getTimtables before creation, to get the latest names and timetable count. Do not assume the timetables are the same since the last query.
- If a user asks for the timetable count, always refetch getTimetables. Assume this count could have changed between user queries.
`,
messages,
tools: {
getTimetables: tool({
description:
"Get all the timetables of the currently logged in user.",
"Get all the timetables of the currently logged in user AND the number of timetables of the currently logged in user",
parameters: z.object({}),
execute: async (args) => {
return await availableFunctions.getTimetables(args, req);
Expand Down
35 changes: 17 additions & 18 deletions course-matrix/backend/src/controllers/timetablesController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,21 @@ export default {
.status(400)
.json({ error: "Timetable Title cannot be over 50 characters long" });
}
// Timetables cannot exceed the size of 25.
const { count: timetable_count, error: timetableCountError } =
await supabase
.schema("timetable")
.from("timetables")
.select("*", { count: "exact", head: true })
.eq("user_id", user_id);

console.log(timetable_count);

if ((timetable_count ?? 0) >= 25) {
return res
.status(400)
.json({ error: "You have exceeded the limit of 25 timetables" });
}

// Check if a timetable with the same title already exist for this user
const { data: existingTimetable, error: existingTimetableError } =
Expand Down Expand Up @@ -163,17 +178,6 @@ export default {
favorite,
email_notifications_enabled,
} = req.body;
if (
!timetable_title &&
!semester &&
favorite === undefined &&
email_notifications_enabled === undefined
) {
return res.status(400).json({
error:
"New timetable title or semester or updated favorite status or email notifications enabled is required when updating a timetable",
});
}

// Timetables cannot be longer than 50 characters.
if (timetable_title && timetable_title.length > 50) {
Expand All @@ -184,7 +188,6 @@ export default {

//Retrieve the authenticated user
const user_id = (req as any).user.id;

//Retrieve users allowed to access the timetable
const { data: timetableUserData, error: timetableUserError } =
await supabase
Expand All @@ -194,15 +197,13 @@ export default {
.eq("user_id", user_id)
.eq("id", id)
.maybeSingle();

if (timetableUserError)
return res.status(400).json({ error: timetableUserError.message });

//Validate timetable validity:
if (!timetableUserData || timetableUserData.length === 0) {
return res.status(404).json({ error: "Calendar id not found" });
}

// Check if another timetable with the same title already exist for this user
const { data: existingTimetable, error: existingTimetableError } =
await supabase
Expand All @@ -213,7 +214,6 @@ export default {
.eq("timetable_title", timetable_title)
.neq("id", id)
.maybeSingle();

if (existingTimetableError) {
return res.status(400).json({ error: existingTimetableError.message });
}
Expand All @@ -223,8 +223,8 @@ export default {
.status(400)
.json({ error: "Another timetable with this title already exists" });
}

let updateData: any = {};
updateData.updated_at = new Date().toISOString();
if (timetable_title) updateData.timetable_title = timetable_title;
if (semester) updateData.semester = semester;
if (favorite !== undefined) updateData.favorite = favorite;
Expand All @@ -240,13 +240,11 @@ export default {
.eq("id", id)
.select()
.single();

const { data: timetableData, error: timetableError } =
await updateTimetableQuery;

if (timetableError)
return res.status(400).json({ error: timetableError.message });

// If no records were updated due to non-existence timetable or it doesn't belong to the user.
if (!timetableData || timetableData.length === 0) {
return res.status(404).json({
Expand All @@ -255,6 +253,7 @@ export default {
}
return res.status(200).json(timetableData);
} catch (error) {
console.error(error);
return res.status(500).send({ error });
}
}),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { describe, expect, it, test } from "@jest/globals";

import { convertTimestampToLocaleTime } from "../src/utils/convert-timestamp-to-locale-time";

describe("convertTimestampToLocaleTime", () => {
test("should convert a valid timestamp string to a locale time string", () => {
const timestamp = "2025-03-28T12:00:00Z";
const result = convertTimestampToLocaleTime(timestamp);
expect(typeof result).toBe("string");
expect(result.length).toBeGreaterThan(0); // Ensures it returns a non-empty string
});

test("should convert a valid numeric timestamp to a locale time string", () => {
const timestamp = 1711622400000; // Equivalent to 2025-03-28T12:00:00Z
// in milliseconds
const result = convertTimestampToLocaleTime(timestamp);
expect(typeof result).toBe("string");
expect(result.length).toBeGreaterThan(0);
});

test("convert to locale time date is different", () => {
const timestamp = "2025-03-28 02:33:02.589Z";
const result = convertTimestampToLocaleTime(timestamp);
expect(typeof result).toBe("string");
expect(result.length).toBeGreaterThan(0);
});

test("should return 'Invalid Date' for an invalid timestamp", () => {
const timestamp = "invalid";
const result = convertTimestampToLocaleTime(timestamp);
expect(result).toBe("Invalid Date");
});
});
2 changes: 1 addition & 1 deletion course-matrix/frontend/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/png" href="/img/logo.png" />
<link rel="icon" type="image/png" href="/img/course-matrix-logo.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Course Matrix </title>
</head>
Expand Down
34 changes: 34 additions & 0 deletions course-matrix/frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions course-matrix/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"@radix-ui/react-separator": "^1.1.2",
"@radix-ui/react-slot": "^1.1.2",
"@radix-ui/react-switch": "^1.1.3",
"@radix-ui/react-toast": "^1.2.6",
"@radix-ui/react-tooltip": "^1.1.8",
"@reduxjs/toolkit": "^2.5.1",
"@schedule-x/drag-and-drop": "^2.21.1",
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added course-matrix/frontend/public/img/grey-avatar.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions course-matrix/frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import SignupPage from "./pages/Signup/SignUpPage";
import AuthRoute from "./components/auth-route";
import SignupSuccessfulPage from "./pages/Signup/SignupSuccessfulPage";
import LoginRoute from "./components/login-route";
import { Toaster } from "./components/ui/toaster";

/**
* App Component
Expand Down Expand Up @@ -44,6 +45,7 @@ function App() {
element={<AuthRoute component={Dashboard} />}
/>
</Routes>
<Toaster />
</div>
);
}
Expand Down
Loading