Skip to content

Commit b816af2

Browse files
committed
Fix incorrect commit so that this branch only contain check timetable duplicated name
1 parent dcb5591 commit b816af2

File tree

10 files changed

+71
-104
lines changed

10 files changed

+71
-104
lines changed

course-matrix/backend/jest.config.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,8 @@ import type { Config } from "jest";
22

33
const config: Config = {
44
preset: "ts-jest",
5-
moduleNameMapper: {
6-
"\\.(css|scss)$": "identity-obj-proxy",
7-
"^.+\\.svg": "<rootDir>/tests/mocks/svgMock.tsx",
8-
},
5+
moduleNameMapper: { "\\.(css|scss)$": "identity-obj-proxy" },
96
// to obtain access to the matchers.
10-
setupFilesAfterEnv: ["<rootDir>/tests/setupTests.ts"],
117
moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"],
128
modulePaths: ["<rootDir>"],
139
testEnvironment: "jsdom",

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

Lines changed: 34 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { Request, Response } from "express";
22
import asyncHandler from "../middleware/asyncHandler";
33
import { supabase } from "../db/setupDb";
4-
import { maybeCoerceBoolean } from "openai/core";
54

65
export default {
76
/**
@@ -19,11 +18,15 @@ export default {
1918
const user_id = (req as any).user.id;
2019

2120
//Retrieve timetable title
22-
const { timetable_title, semester, favorite = false } = req.body;
23-
if (!timetable_title || !semester) {
21+
const { timetable_title, semester } = req.body;
22+
if (!timetable_title) {
23+
return res.status(400).json({ error: "timetable title is required" });
24+
}
25+
26+
if (!semester) {
2427
return res
2528
.status(400)
26-
.json({ error: "timetable title and semester are required" });
29+
.json({ error: "timetable semester is required" });
2730
}
2831

2932
// Check if a timetable with the same title already exist for this user
@@ -50,14 +53,7 @@ export default {
5053
let insertTimetable = supabase
5154
.schema("timetable")
5255
.from("timetables")
53-
.insert([
54-
{
55-
user_id,
56-
timetable_title,
57-
semester,
58-
favorite,
59-
},
60-
])
56+
.insert([{ user_id, timetable_title, semester }])
6157
.select()
6258
.single();
6359

@@ -117,11 +113,11 @@ export default {
117113
const { id } = req.params;
118114

119115
//Retrieve timetable title
120-
const { timetable_title, semester, favorite } = req.body;
121-
if (!timetable_title && !semester && favorite === undefined) {
116+
const { timetable_title, semester } = req.body;
117+
if (!timetable_title && !semester) {
122118
return res.status(400).json({
123119
error:
124-
"New timetable title or semester or updated favorite status is required when updating a timetable",
120+
"New timetable title or semester is required when updating a timetable",
125121
});
126122
}
127123

@@ -138,34 +134,26 @@ export default {
138134
.eq("user_id", user_id)
139135
.maybeSingle();
140136

141-
if (timetableUserError || !timetableUserData)
137+
const timetable_user_id = timetableUserData?.user_id;
138+
139+
if (timetableUserError)
140+
return res.status(400).json({ error: timetableUserError.message });
141+
142+
//Validate timetable validity:
143+
if (!timetableUserData || timetableUserData.length === 0) {
144+
return res.status(404).json({ error: "Calendar id not found" });
145+
}
146+
147+
//Validate user access
148+
if (user_id !== timetable_user_id) {
142149
return res
143-
.status(400)
144-
.json({ error: "Timetable not found or unauthorized" });
145-
146-
// Check for duplicate timetable title
147-
if (timetable_title) {
148-
const { data: existingTimetable, error: existingTimetableError } =
149-
await supabase
150-
.schema("timetable")
151-
.from("timetables")
152-
.select("*")
153-
.eq("user_id", user_id)
154-
.eq("timetable_title", timetable_title)
155-
.neq("id", id)
156-
.maybeSingle();
157-
158-
if (existingTimetable) {
159-
return res
160-
.status(400)
161-
.json({ error: "A timetable this title already exist" });
162-
}
150+
.status(401)
151+
.json({ error: "Unauthorized access to timetable events" });
163152
}
164153

165154
let updateData: any = {};
166155
if (timetable_title) updateData.timetable_title = timetable_title;
167156
if (semester) updateData.semester = semester;
168-
if (favorite !== undefined) updateData.favorite = favorite;
169157

170158
//Update timetable title, for authenticated user only
171159
let updateTimetableQuery = supabase
@@ -174,8 +162,7 @@ export default {
174162
.update(updateData)
175163
.eq("id", id)
176164
.eq("user_id", user_id)
177-
.select()
178-
.single();
165+
.select();
179166

180167
const { data: timetableData, error: timetableError } =
181168
await updateTimetableQuery;
@@ -215,6 +202,7 @@ export default {
215202
.eq("id", id)
216203
.eq("user_id", user_id)
217204
.maybeSingle();
205+
const timetable_user_id = timetableUserData?.user_id;
218206

219207
if (timetableUserError)
220208
return res.status(400).json({ error: timetableUserError.message });
@@ -224,6 +212,13 @@ export default {
224212
return res.status(404).json({ error: "Calendar id not found" });
225213
}
226214

215+
//Validate user access
216+
if (user_id !== timetable_user_id) {
217+
return res
218+
.status(401)
219+
.json({ error: "Unauthorized access to timetable events" });
220+
}
221+
227222
// Delete only if the timetable belongs to the authenticated user
228223
let deleteTimetableQuery = supabase
229224
.schema("timetable")

course-matrix/backend/tests/mocks/svgMock.tsx

Lines changed: 0 additions & 2 deletions
This file was deleted.

course-matrix/backend/tests/setupTests.ts

Lines changed: 0 additions & 1 deletion
This file was deleted.

course-matrix/backend/tsconfig.app.json

Lines changed: 0 additions & 31 deletions
This file was deleted.

course-matrix/frontend/src/pages/TimetableBuilder/TimetableBuilder.tsx

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,9 @@ const TimetableBuilder = () => {
104104
const [isCustomSettingsOpen, setIsCustomSettingsOpen] = useState(false);
105105
const [filters, setFilters] = useState<FilterForm | null>(null);
106106
const [showFilters, setShowFilters] = useState(false);
107-
const [timetableId, setTimetableId] = useState(-1);
107+
const [timetableId, setTimetableId] = useState(
108+
editingTimetableId ? parseInt(editingTimetableId) : 0
109+
);
108110

109111
const noSearchAndFilter = () => {
110112
return !searchQuery && !filters;
@@ -120,7 +122,7 @@ const TimetableBuilder = () => {
120122
});
121123

122124
const { data: eventsData, isLoading: eventsLoading } = useGetEventsQuery(
123-
timetableId,
125+
timetableId
124126
) as {
125127
data: { courseEvents: unknown[]; userEvents: unknown[] };
126128
isLoading: boolean;
@@ -254,7 +256,7 @@ const TimetableBuilder = () => {
254256
<SelectItem key={value} value={value}>
255257
{value}
256258
</SelectItem>
257-
),
259+
)
258260
)}
259261
</SelectContent>
260262
</Select>
@@ -290,7 +292,7 @@ const TimetableBuilder = () => {
290292
</div>
291293
<div className="flex flex-col">
292294
<p className="text-sm pb-2">
293-
Selected courses: {selectedCourses.length}
295+
Selected courses: {selectedCourses.length} (Max 8)
294296
</p>
295297
<div className="flex gap-2 flex-col">
296298
{selectedCourses.map((course, index) => (
@@ -330,9 +332,18 @@ const TimetableBuilder = () => {
330332
</div>
331333

332334
<div className="flex flex-col">
333-
<p className="text-sm pb-2">
334-
Enabled Restrictions: {enabledRestrictions.length}
335-
</p>
335+
<FormField
336+
control={form.control}
337+
name="restrictions"
338+
render={({ field }) => (
339+
<FormItem className="pb-2">
340+
<p className="text-sm">
341+
Enabled Restrictions: {enabledRestrictions.length}
342+
</p>
343+
<FormMessage />
344+
</FormItem>
345+
)}
346+
/>
336347
<div className="flex gap-2 flex-col">
337348
{enabledRestrictions.map((restric, index) => (
338349
<div
@@ -370,18 +381,19 @@ const TimetableBuilder = () => {
370381

371382
<Button type="submit">Generate</Button>
372383
</form>
384+
385+
{isCustomSettingsOpen && (
386+
<CreateCustomSetting
387+
submitHandler={handleAddRestriction}
388+
closeHandler={() => setIsCustomSettingsOpen(false)}
389+
/>
390+
)}
373391
</FormContext.Provider>
374392
</Form>
375393
</div>
376394
<div className="w-3/5">
377395
<Calendar courseEvents={courseEvents} userEvents={userEvents} />
378396
</div>
379-
{isCustomSettingsOpen && (
380-
<CreateCustomSetting
381-
submitHandler={handleAddRestriction}
382-
closeHandler={() => setIsCustomSettingsOpen(false)}
383-
/>
384-
)}
385397

386398
{showFilters && (
387399
<SearchFilters

course-matrix/frontend/tests/mocks/svgMock.tsx

Lines changed: 0 additions & 2 deletions
This file was deleted.

course-matrix/frontend/tests/setupTests.ts

Lines changed: 0 additions & 1 deletion
This file was deleted.

course-matrix/frontend/tsconfig.app.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,5 +27,5 @@
2727
"@/*": ["./src/*"]
2828
}
2929
},
30-
"include": ["src", "tests/setupTests.ts"]
30+
"include": ["src"]
3131
}
Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
{
2+
"files": [],
3+
"references": [
4+
{ "path": "./tsconfig.app.json" },
5+
{ "path": "./tsconfig.node.json" }
6+
],
27
"compilerOptions": {
3-
"target": "es6",
4-
"module": "commonjs",
5-
"outDir": "./build",
6-
"strict": true,
7-
"esModuleInterop": true,
8-
"skipLibCheck": true
9-
},
10-
"include": ["src/**/*"],
11-
"exclude": ["node_modules", "tests"]
8+
"baseUrl": ".",
9+
"paths": {
10+
"@/*": ["./src/*"]
11+
}
12+
}
1213
}

0 commit comments

Comments
 (0)