Skip to content

Commit aac3584

Browse files
MerryRosalieahnaflolaimenhamed
authored
feat(frontend)/ELEC-496: Revamp User Profile Page (#222)
* add initial oauth support * feat: finished styling of the user page with dummy datas * feat: added option to add headers to request util function in the frontend * feat: finished list view for reviews * feat: pagination implemented and edit report modal draft * fix: change request schema reviews/:reviewId to the appropriate schema * feat: removed the use of rating and replace it to overallRating in the frontend, implemented edit review modal * feat: implemented remove review modal * fix: fixed lint issues related to key prop element in iterations * add federated authentication support * add federated authentication support * add comment for module augmentation * create middleware * add initial oauth support * remove auth server remove login window remove validation on course page * remove auth server modify docker * feat: optimistic ui update for edit and delete review * chore: fix backend test * chore: removed login route * added fixes for functionality * more fixes * feat: implemented optimistic ui updates on upvotes and posting new reviews and added asterisks on required field on the review modal * added route for getting bookedmarked courses if logged in * feat: optimistic ui update on bookmarking reviews, added success type to alert, fixed rating where it previously used px instead of %, and fixed issues on sorting when adding a review * fix: fixed issues with optimistic ui update on bookmark * fix: minor ui/ux fix --------- Co-authored-by: Ahnaf Tazwar <ahnaftazwar368@gmail.com> Co-authored-by: Aimen Hamed <aimenhamed@live.com.au>
1 parent 05de1cc commit aac3584

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+2169
-622
lines changed

backend/prisma/schema.prisma

Lines changed: 53 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -15,66 +15,66 @@ enum report_status {
1515
}
1616

1717
model courses {
18-
courseCode String @id(map: "pk_course_code") @map("course_code")
19-
archived Boolean
20-
attributes String[]
21-
calendar String
22-
campus String
23-
description String
24-
enrolmentRules String @map("enrolment_rules")
25-
equivalents String[]
26-
exclusions String[]
27-
faculty String
28-
fieldOfEducation String @map("field_of_education")
29-
genEd Boolean @map("gen_ed")
30-
level Int
31-
school String
32-
studyLevel String @map("study_level")
33-
terms Int[]
34-
title String
35-
uoc Int
36-
rating Float
37-
reviews reviews[]
18+
courseCode String @id(map: "pk_course_code") @map("course_code")
19+
archived Boolean
20+
attributes String[]
21+
calendar String
22+
campus String
23+
description String
24+
enrolmentRules String @map("enrolment_rules")
25+
equivalents String[]
26+
exclusions String[]
27+
faculty String
28+
fieldOfEducation String @map("field_of_education")
29+
genEd Boolean @map("gen_ed")
30+
level Int
31+
school String
32+
studyLevel String @map("study_level")
33+
terms Int[]
34+
title String
35+
uoc Int
36+
rating Float
37+
reviews reviews[]
3838
}
3939

4040
model reports {
41-
reportId String @id(map: "pk_report_id") @default(dbgenerated("gen_random_uuid()")) @db.Uuid @map("report_id")
42-
reviewId String @db.Uuid @map("review_id")
43-
zid String
44-
status report_status
45-
reason String
46-
createdTimestamp DateTime @default(now()) @db.Timestamp(6) @map("created_timestamp")
47-
updatedTimestamp DateTime @default(now()) @db.Timestamp(6) @map("updated_timestamp")
48-
reviews reviews @relation(fields: [reviewId], references: [reviewId], onDelete: NoAction, onUpdate: NoAction, map: "fk_review_id")
49-
users users @relation(fields: [zid], references: [zid], onDelete: NoAction, onUpdate: NoAction, map: "fk_zid")
41+
reportId String @id(map: "pk_report_id") @default(dbgenerated("gen_random_uuid()")) @map("report_id") @db.Uuid
42+
reviewId String @map("review_id") @db.Uuid
43+
zid String
44+
status report_status
45+
reason String
46+
createdTimestamp DateTime @default(now()) @map("created_timestamp") @db.Timestamp(6)
47+
updatedTimestamp DateTime @default(now()) @map("updated_timestamp") @db.Timestamp(6)
48+
reviews reviews @relation(fields: [reviewId], references: [reviewId], onDelete: NoAction, onUpdate: NoAction, map: "fk_review_id")
49+
users users @relation(fields: [zid], references: [zid], onDelete: NoAction, onUpdate: NoAction, map: "fk_zid")
5050
}
5151

5252
model reviews {
53-
reviewId String @id(map: "pk_review_id") @default(dbgenerated("gen_random_uuid()")) @db.Uuid @map("review_id")
54-
zid String
55-
courseCode String @map("course_code")
56-
authorName String @map("author_name")
57-
title String
58-
description String?
59-
grade Int?
60-
termTaken String @map("term_taken")
61-
createdTimestamp DateTime @default(now()) @db.Timestamp(6) @map("created_timestamp")
62-
updatedTimestamp DateTime @default(now()) @db.Timestamp(6) @map("updated_timestamp")
63-
upvotes String[]
64-
manageability Float
65-
usefulness Float
66-
enjoyability Float
67-
overallRating Float @map("overall_rating")
68-
reports reports[]
69-
courses courses @relation(fields: [courseCode], references: [courseCode], onDelete: NoAction, onUpdate: NoAction, map: "fk_course_code")
70-
users users @relation(fields: [zid], references: [zid], onDelete: NoAction, onUpdate: NoAction, map: "fk_zid")
53+
reviewId String @id(map: "pk_review_id") @default(dbgenerated("gen_random_uuid()")) @map("review_id") @db.Uuid
54+
zid String
55+
courseCode String @map("course_code")
56+
authorName String @map("author_name")
57+
title String
58+
description String?
59+
grade Int?
60+
termTaken String @map("term_taken")
61+
createdTimestamp DateTime @default(now()) @map("created_timestamp") @db.Timestamp(6)
62+
updatedTimestamp DateTime @default(now()) @map("updated_timestamp") @db.Timestamp(6)
63+
upvotes String[]
64+
manageability Float
65+
usefulness Float
66+
enjoyability Float
67+
overallRating Float @map("overall_rating")
68+
reports reports[]
69+
courses courses @relation(fields: [courseCode], references: [courseCode], onDelete: NoAction, onUpdate: NoAction, map: "fk_course_code")
70+
users users @relation(fields: [zid], references: [zid], onDelete: NoAction, onUpdate: NoAction, map: "fk_zid")
7171
}
7272

7373
model users {
74-
zid String @id(map: "pk_zid")
75-
bookmarkedReviews String[] @map("bookmarked_reviews")
76-
bookmarkedCourses String[] @map("bookmarked_courses")
77-
isAdmin Boolean @map("is_admin")
78-
reports reports[]
79-
reviews reviews[]
74+
zid String @id(map: "pk_zid")
75+
bookmarkedReviews String[] @map("bookmarked_reviews")
76+
bookmarkedCourses String[] @map("bookmarked_courses")
77+
isAdmin Boolean @map("is_admin")
78+
reports reports[]
79+
reviews reviews[]
8080
}

backend/src/App.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import { UserService } from "./services/user.service";
1010
import { UserController } from "./controllers/user.controller";
1111
import { ReportController } from "./controllers/report.controller";
1212
import { ReportService } from "./services/report.service";
13-
import { AuthService } from "./modules/Auth";
1413
import { CourseRepository } from "./repositories/course.repository";
1514
import { UserRepository } from "./repositories/user.repository";
1615
import PrismaClient from "./modules/prisma";
@@ -23,9 +22,6 @@ export default class App {
2322
private prisma = new PrismaClient();
2423
private redis = new RedisClient();
2524

26-
// auth
27-
private readonly auth = new AuthService();
28-
2925
private readonly courseRepository = new CourseRepository(
3026
this.prisma.getConnection(),
3127
);
@@ -46,8 +42,9 @@ export default class App {
4642
this.redis,
4743
);
4844
private readonly userService = new UserService(
49-
this.auth,
5045
this.userRepository,
46+
this.reviewRepository,
47+
this.redis,
5148
);
5249
private readonly reportService = new ReportService(
5350
this.reportRepository,

backend/src/api/schemas/review.schema.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ export const PostReviewSchema = z
1919
zid: z.string(),
2020
authorName: z.string(),
2121
title: z.string(),
22-
description: z.string(),
22+
description: z.string().nullable(),
23+
grade: z.number().nullable(),
2324
courseCode: z.string(),
24-
rating: z.number(),
2525
termTaken: z.string(),
2626
manageability: z.number(),
2727
usefulness: z.number(),
@@ -70,7 +70,7 @@ const PostReviewRequestBodySchema = z
7070

7171
export type PostReviewRequestBody = z.infer<typeof PostReviewRequestBodySchema>;
7272

73-
const PutReviewRequestBodySchema = z
73+
export const PutReviewRequestBodySchema = z
7474
.object({
7575
authorName: z.string(),
7676
grade: z.number().nullable(),

backend/src/controllers/course.controller.ts

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -78,33 +78,6 @@ export class CourseController implements IController {
7878
}
7979
},
8080
)
81-
.post(
82-
"/courses/bookmark",
83-
validationMiddleware(BookmarkCourseSchema, "body"),
84-
async (
85-
req: Request<Record<string, never>, unknown, BookmarkCourse>,
86-
res: Response,
87-
next: NextFunction,
88-
) => {
89-
this.logger.debug(`Received request in POST /courses/bookmark`);
90-
try {
91-
const bookmarkDetails = req.body;
92-
if (!bookmarkDetails) throw new HTTPError(badRequest);
93-
const result = await this.courseService.bookmarkCourse(
94-
bookmarkDetails,
95-
);
96-
this.logger.info(`Responding to client in POST /courses/bookmark`);
97-
return res.status(200).json(result);
98-
} catch (err: any) {
99-
this.logger.warn(
100-
`An error occurred when trying to POST /courses/bookmark ${formatError(
101-
err,
102-
)}`,
103-
);
104-
return next(err);
105-
}
106-
},
107-
)
10881
.get(
10982
"/course/:courseCode",
11083
async (

backend/src/controllers/review.controller.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
PutReviewRequestBody,
1414
BookmarkReview,
1515
UpvoteReview,
16+
PutReviewRequestBodySchema,
1617
} from "../api/schemas/review.schema";
1718

1819
export class ReviewController implements IController {
@@ -97,7 +98,7 @@ export class ReviewController implements IController {
9798
)
9899
.put(
99100
"/reviews/:reviewId",
100-
validationMiddleware(PostReviewSchema, "body"),
101+
validationMiddleware(PutReviewRequestBodySchema, "body"),
101102
async (
102103
req: Request<{ reviewId: string }, unknown, PutReviewRequestBody>,
103104
res: Response,

backend/src/controllers/user.controller.ts

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -44,47 +44,56 @@ export class UserController implements IController {
4444
}
4545
},
4646
)
47-
.post(
48-
"/user/login",
49-
validationMiddleware(CreateUserSchema, "body"),
47+
.get(
48+
"/user/:zid",
49+
[verifyToken],
5050
async (
51-
req: Request<Record<string, never>, unknown, CreateUser>,
51+
req: Request<{ zid: string }, unknown>,
5252
res: Response,
5353
next: NextFunction,
5454
) => {
55-
const { zid } = req.body;
56-
this.logger.debug(`Received POST request in /user/login`, req.body);
55+
const { zid } = req.params;
56+
this.logger.debug(`Received GET request in /user`, req.params);
5757
try {
58-
const result = await this.userService.loginUser(zid);
59-
this.logger.info(`Responding to client in /user/login`);
58+
const result = await this.userService.getUser(zid);
59+
this.logger.info(`Responding to client in /user/${zid}`);
6060
return res.status(200).json(result);
6161
} catch (err: any) {
6262
this.logger.warn(
63-
`An error occurred when trying to POST /user/login ${formatError(
64-
err,
65-
)}`,
63+
`An error occurred when trying to GET /user ${formatError(err)}`,
6664
);
6765
return next(err);
6866
}
6967
},
7068
)
7169
.get(
72-
"/user/:zid",
70+
"/user/course/:courseCode",
7371
[verifyToken],
7472
async (
75-
req: Request<{ zid: string }, unknown>,
73+
req: Request<{ courseCode: string }, unknown>,
7674
res: Response,
7775
next: NextFunction,
7876
) => {
79-
const { zid } = req.params;
80-
this.logger.debug(`Received GET request in /user`, req.params);
77+
const { courseCode } = req.params;
78+
const zid = req.headers.zid as string;
79+
this.logger.debug(
80+
`Received GET request in /user/course/:courseCode`,
81+
req.params,
82+
);
8183
try {
82-
const result = await this.userService.getUser(zid);
83-
this.logger.info(`Responding to client in /user/${zid}`);
84+
const result = await this.userService.getUserCourseInfo(
85+
courseCode,
86+
zid,
87+
);
88+
this.logger.info(
89+
`Responding to client in /user/course/:courseCode`,
90+
);
8491
return res.status(200).json(result);
8592
} catch (err: any) {
8693
this.logger.warn(
87-
`An error occurred when trying to GET /user ${formatError(err)}`,
94+
`An error occurred when trying to GET /user/course/:courseCode ${formatError(
95+
err,
96+
)}`,
8897
);
8998
return next(err);
9099
}

backend/src/modules/Auth.ts

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

backend/src/repositories/report.repository.ts

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,7 @@ export class ReportRepository {
3232
return reports;
3333
}
3434

35-
async getReportByUserAndReview(
36-
zid: string,
37-
reviewId: string,
38-
): Promise<Report | null> {
35+
async getReportByUserAndReview(zid: string, reviewId: string) {
3936
const rawReport = await this.prisma.reports.findFirst({
4037
where: {
4138
zid: zid,
@@ -45,11 +42,10 @@ export class ReportRepository {
4542
users: true,
4643
},
4744
});
48-
const report = ReportSchema.parse(rawReport);
49-
return report;
45+
return rawReport;
5046
}
5147

52-
async getReport(reportId: string): Promise<Report | null> {
48+
async getReport(reportId: string) {
5349
const rawReport = await this.prisma.reports.findUnique({
5450
where: {
5551
reportId: reportId,
@@ -58,11 +54,10 @@ export class ReportRepository {
5854
users: true,
5955
},
6056
});
61-
const report = ReportSchema.parse(rawReport);
62-
return report;
57+
return rawReport;
6358
}
6459

65-
async newReport(report: CreateReport): Promise<Report> {
60+
async newReport(report: CreateReport) {
6661
const rawReport = await this.prisma.reports.create({
6762
data: {
6863
...report,
@@ -72,8 +67,7 @@ export class ReportRepository {
7267
users: true,
7368
},
7469
});
75-
const savedReport = ReportSchema.parse(rawReport);
76-
return savedReport;
70+
return rawReport;
7771
}
7872

7973
async saveReport(report: UpdateReportStatus): Promise<Report> {

backend/src/repositories/review.repository.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,33 @@ export class ReviewRepository {
2424
});
2525
}
2626

27+
async update(review: {
28+
reviewId: string;
29+
grade: number | null;
30+
authorName: string;
31+
}) {
32+
return await this.prisma.reviews.update({
33+
where: {
34+
reviewId: review.reviewId,
35+
},
36+
data: {
37+
grade: review.grade,
38+
authorName: review.authorName,
39+
},
40+
});
41+
}
42+
43+
async updateUpvotes(review: { reviewId: string; upvotes: string[] }) {
44+
return await this.prisma.reviews.update({
45+
where: {
46+
reviewId: review.reviewId,
47+
},
48+
data: {
49+
upvotes: review.upvotes,
50+
},
51+
});
52+
}
53+
2754
async getReviewsByUser(zid: string): Promise<reviews[]> {
2855
return await this.prisma.reviews.findMany({
2956
where: {

0 commit comments

Comments
 (0)