Skip to content
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
27e31fa
Update team.md
thomasyzy7 Jan 23, 2025
daad2a4
Merge pull request #3 from UTSC-CSCC01-Software-Engineering-I/develop
Austin-X Jan 25, 2025
761a7eb
This commit tests if the main branch actually blocks force-pushes
Austin-X Jan 29, 2025
70578b8
Revert "This commit tests if the main branch actually blocks force-pu…
Austin-X Jan 29, 2025
a21f3e3
Release/0.1 - Main (#14)
thomasyzy7 Feb 1, 2025
ff98df3
[Hotfix]: Add missing section in README.md for "Tech Stack and Softwa…
Austin-X Feb 1, 2025
5024884
Release/1.0 - Main (#30)
thomasyzy7 Feb 15, 2025
813595b
Added Sprint0 Rubric + Grades + Deductions (#39)
prokopchukdim Feb 20, 2025
6def085
Added Sprint 1 Rubric / Grades (#49)
prokopchukdim Mar 2, 2025
fccb706
Merge branch 'main' into release/1.2
Austin-X Mar 8, 2025
9f8cde4
Auto-formatted the code using Prettier
Austin-X Mar 8, 2025
3adc27c
Fix typo
Austin-X Mar 8, 2025
2699f2a
Release/1.2 - Main (#60)
thomasyzy7 Mar 8, 2025
4cb5b39
Improved querying of year level and breadth requirement from vector db
kevin-lann Mar 10, 2025
f06912f
Auto-formatted the code using Prettier
kevin-lann Mar 10, 2025
7821a3a
Merge branch 'kl/scrum-132-refine-ai-outputs' of https://github.com/U…
minhhaitran08 Mar 11, 2025
aabd2f4
Merge branch 'main' of https://github.com/UTSC-CSCC01-Software-Engine…
minhhaitran08 Mar 12, 2025
eca6fc9
Update Create and get timetable end points to include favorite field
minhhaitran08 Mar 12, 2025
ef9771b
Auto-formatted the code using Prettier
minhhaitran08 Mar 12, 2025
74b36fc
Update endpoints to check for duplicated names
minhhaitran08 Mar 13, 2025
dcb5591
Fix typo
minhhaitran08 Mar 15, 2025
b816af2
Fix incorrect commit so that this branch only contain check timetable…
minhhaitran08 Mar 16, 2025
5c28f25
Auto-formatted the code using Prettier
minhhaitran08 Mar 16, 2025
08b9b48
Fix incorrect commit
minhhaitran08 Mar 16, 2025
94d69ed
Merge branch 'mt/scrum-137-check-timetable-duplicated-name' of https:…
minhhaitran08 Mar 16, 2025
4d28b1c
Merge branch 'develop' into mt/scrum-137-check-timetable-duplicated-name
minhhaitran08 Mar 16, 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
6 changes: 5 additions & 1 deletion course-matrix/backend/jest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@ import type { Config } from "jest";

const config: Config = {
preset: "ts-jest",
moduleNameMapper: { "\\.(css|scss)$": "identity-obj-proxy" },
moduleNameMapper: {
"\\.(css|scss)$": "identity-obj-proxy",
"^.+\\.svg": "<rootDir>/tests/mocks/svgMock.tsx",
},
// to obtain access to the matchers.
setupFilesAfterEnv: ["<rootDir>/tests/setupTests.ts"],
moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"],
modulePaths: ["<rootDir>"],
testEnvironment: "jsdom",
Expand Down
93 changes: 60 additions & 33 deletions course-matrix/backend/src/controllers/timetablesController.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Request, Response } from "express";
import asyncHandler from "../middleware/asyncHandler";
import { supabase } from "../db/setupDb";
import { maybeCoerceBoolean } from "openai/core";

export default {
/**
Expand All @@ -18,26 +19,51 @@ export default {
const user_id = (req as any).user.id;

//Retrieve timetable title
const { timetable_title, semester } = req.body;
if (!timetable_title) {
return res.status(400).json({ error: "timetable title is required" });
const { timetable_title, semester, favorite = false } = req.body;
if (!timetable_title || !semester) {
return res
.status(400)
.json({ error: "timetable title and semester are required" });
}

if (!semester) {
// Check if a timetable with the same title already exist for this user
const { data: existingTimetable, error: existingTimetableError } =
await supabase
.schema("timetable")
.from("timetables")
.select("id")
.eq("user_id", user_id)
.eq("timetable_title", timetable_title)
.maybeSingle();

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

if (existingTimetable) {
return res
.status(400)
.json({ error: "timetable semester is required" });
.json({ error: "A timetable with this title already exists" });
}

//Create query to insert the user_id and timetable_title into the db
let insertTimetable = supabase
.schema("timetable")
.from("timetables")
.insert([{ user_id, timetable_title, semester }])
.select();
.insert([
{
user_id,
timetable_title,
semester,
favorite,
},
])
.select()
.single();

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

if (timetableError) {
return res.status(400).json({ error: timetableError.message });
}
Expand Down Expand Up @@ -91,11 +117,11 @@ export default {
const { id } = req.params;

//Retrieve timetable title
const { timetable_title, semester } = req.body;
if (!timetable_title && !semester) {
const { timetable_title, semester, favorite } = req.body;
if (!timetable_title && !semester && favorite === undefined) {
return res.status(400).json({
error:
"New timetable title or semester is required when updating a timetable",
"New timetable title or semester or updated favorite status is required when updating a timetable",
});
}

Expand All @@ -112,26 +138,34 @@ export default {
.eq("user_id", user_id)
.maybeSingle();

const timetable_user_id = timetableUserData?.user_id;

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" });
}

//Validate user access
if (user_id !== timetable_user_id) {
if (timetableUserError || !timetableUserData)
return res
.status(401)
.json({ error: "Unauthorized access to timetable events" });
.status(400)
.json({ error: "Timetable not found or unauthorized" });

// Check for duplicate timetable title
if (timetable_title) {
const { data: existingTimetable, error: existingTimetableError } =
await supabase
.schema("timetable")
.from("timetables")
.select("*")
.eq("user_id", user_id)
.eq("timetable_title", timetable_title)
.neq("id", id)
.maybeSingle();

if (existingTimetable) {
return res
.status(400)
.json({ error: "A timetable this title already exist" });
}
}

let updateData: any = {};
if (timetable_title) updateData.timetable_title = timetable_title;
if (semester) updateData.semester = semester;
if (favorite !== undefined) updateData.favorite = favorite;

//Update timetable title, for authenticated user only
let updateTimetableQuery = supabase
Expand All @@ -140,7 +174,8 @@ export default {
.update(updateData)
.eq("id", id)
.eq("user_id", user_id)
.select();
.select()
.single();

const { data: timetableData, error: timetableError } =
await updateTimetableQuery;
Expand Down Expand Up @@ -180,7 +215,6 @@ export default {
.eq("id", id)
.eq("user_id", user_id)
.maybeSingle();
const timetable_user_id = timetableUserData?.user_id;

if (timetableUserError)
return res.status(400).json({ error: timetableUserError.message });
Expand All @@ -190,13 +224,6 @@ export default {
return res.status(404).json({ error: "Calendar id not found" });
}

//Validate user access
if (user_id !== timetable_user_id) {
return res
.status(401)
.json({ error: "Unauthorized access to timetable events" });
}

// Delete only if the timetable belongs to the authenticated user
let deleteTimetableQuery = supabase
.schema("timetable")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export const convertBreadthRequirement = (code: string) => {
if (code === "ART_LIT_LANG") return "Arts, Literature and Language";
else if (code === "HIS_PHIL_CUL")
return "History, Philosophy and Cultural Studies";
else if (code === "SOCIAL_SCI") return "Social and Behavioral Sciences";
else if (code === "NAT_SCI") return "Natural Sciences";
else if (code === "QUANT") return "Quantitative Reasoning";
else return "";
};
7 changes: 7 additions & 0 deletions course-matrix/backend/src/utils/convert-year-level.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const convertYearLevel = (code: string) => {
if (code === "first_year") return "1st year";
else if (code === "second_year") return "2nd year";
else if (code === "third_year") return "3rd year";
else if (code === "fourth_year") return "4th year";
else return "";
};
2 changes: 2 additions & 0 deletions course-matrix/backend/tests/mocks/svgMock.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export default "SvgrURL";
export const ReactComponent = "div";
1 change: 1 addition & 0 deletions course-matrix/backend/tests/setupTests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import "@testing-library/jest-dom";
31 changes: 31 additions & 0 deletions course-matrix/backend/tsconfig.app.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,

/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
"jsx": "react-jsx",

/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true,

"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["src", "tests/setupTests.ts"]
}
6 changes: 5 additions & 1 deletion course-matrix/frontend/jest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@ import type { Config } from "jest";

const config: Config = {
preset: "ts-jest",
moduleNameMapper: { "\\.(css|scss)$": "identity-obj-proxy" },
moduleNameMapper: {
"\\.(css|scss)$": "identity-obj-proxy",
"^.+\\.svg": "<rootDir>/tests/mocks/svgMock.tsx",
},
// to obtain access to the matchers.
setupFilesAfterEnv: ["<rootDir>/tests/setupTests.ts"],
moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"],
modulePaths: ["<rootDir>"],
testEnvironment: "jsdom",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,9 +104,7 @@ const TimetableBuilder = () => {
const [isCustomSettingsOpen, setIsCustomSettingsOpen] = useState(false);
const [filters, setFilters] = useState<FilterForm | null>(null);
const [showFilters, setShowFilters] = useState(false);
const [timetableId, setTimetableId] = useState(
editingTimetableId ? parseInt(editingTimetableId) : 0,
);
const [timetableId, setTimetableId] = useState(-1);

const noSearchAndFilter = () => {
return !searchQuery && !filters;
Expand Down
2 changes: 2 additions & 0 deletions course-matrix/frontend/tests/mocks/svgMock.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export default "SvgrURL";
export const ReactComponent = "div";
1 change: 1 addition & 0 deletions course-matrix/frontend/tests/setupTests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import "@testing-library/jest-dom";
2 changes: 1 addition & 1 deletion course-matrix/frontend/tsconfig.app.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,5 @@
"@/*": ["./src/*"]
}
},
"include": ["src"]
"include": ["src", "tests/setupTests.ts"]
}
19 changes: 9 additions & 10 deletions course-matrix/frontend/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
{
"files": [],
"references": [
{ "path": "./tsconfig.app.json" },
{ "path": "./tsconfig.node.json" }
],
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
}
"target": "es6",
"module": "commonjs",
"outDir": "./build",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "tests"]
}