Summary
The reviews feature added in #18 is complete on the server and all but
unreachable in the browser.
The API exposes a full set of endpoints, and GET /api/reviews/:courseId is
deliberately public — no authMiddleware on that line:
router.get("/summaries", getRatingSummaries);
router.get("/:courseId", listReviews);
router.get("/:courseId/summary", getRatingSummary);
router.get("/:courseId/mine", authMiddleware, getMyReview);
router.post("/:courseId", authMiddleware, createReview);
router.put("/review/:reviewId", authMiddleware, updateReview);
router.delete("/review/:reviewId", authMiddleware, deleteReview);
createReview gates on enrolment and nothing else:
const enrollment = await ensureEnrollment(userId, courseId);
if (!enrollment) {
return res.status(403).send({
success: false,
message: "Only enrolled students can review this course.",
});
}
The <CourseReviews> component that consumes all of this is rendered in
exactly one place in the entire frontend —
frontend/src/components/user/student/CourseContent.jsx:394
— inside <Modal.Body> of the certificate modal. That modal is opened by a
button that only renders behind isComplete:
{isComplete ? (
...
onClick={() => setShowModal(true)}
So the only route to reading or writing a review is: enrol, complete every
section, click Get Certificate, and scroll past the certificate. Two things
follow.
Nobody can read reviews. Every catalogue card renders a
<CourseRatingBadge> showing a star average and a count. It is not a link and
it has no expanded form. A prospective student is shown "4.6 (23)" and given
no way to see any of the 23 reviews, on the one screen where reviews exist to
inform a decision. The public listReviews endpoint has no caller in the
frontend at all — CourseReviews is the only consumer and it is behind a
completion gate.
Most enrolled students cannot write one. The server's rule is enrolment;
the UI's rule is 100% completion. A student who has watched nine of ten
sections is authorised by the API and has no button. Reviews are therefore
collected only from the subset who finish courses, which biases every average
on the catalogue upward — and the bias is invisible because the count looks
like it covers all enrolments.
Steps to reproduce
- Sign in as a student and open the catalogue. Note a course card showing a
star rating.
- Click the rating badge, the stars, the count — nothing is interactive, and
there is no course detail page to navigate to.
- Enrol in the course and open it. Complete some but not all sections.
- There is nowhere on the player to leave a review.
GET /api/reviews/<courseId> in a terminal returns the reviews the UI will
not show you.
Expected result
Reviews are readable wherever a rating is shown, without an account. Any
enrolled student can leave, edit and delete a review from the course player,
matching the rule the API enforces.
Actual result
Reviews are write-once-at-100%-completion and readable by nobody. A public
endpoint has no consumer, and a badge advertises data that has no route to the
screen.
Suggested fix
Move <CourseReviews> out of the certificate modal and onto the course player
itself, where any enrolled student reaches it. Make the catalogue's rating
badge open the reviews for that course — the summaries are already batched by
useRatingSummaries, so the list is one request on demand and nothing on the
catalogue's critical path. Leave the certificate modal's congratulation
intact; it is a reasonable prompt, just not the only door.
Summary
The reviews feature added in #18 is complete on the server and all but
unreachable in the browser.
The API exposes a full set of endpoints, and
GET /api/reviews/:courseIdisdeliberately public — no
authMiddlewareon that line:createReviewgates on enrolment and nothing else:The
<CourseReviews>component that consumes all of this is rendered inexactly one place in the entire frontend —
— inside
<Modal.Body>of the certificate modal. That modal is opened by abutton that only renders behind
isComplete:So the only route to reading or writing a review is: enrol, complete every
section, click Get Certificate, and scroll past the certificate. Two things
follow.
Nobody can read reviews. Every catalogue card renders a
<CourseRatingBadge>showing a star average and a count. It is not a link andit has no expanded form. A prospective student is shown "4.6 (23)" and given
no way to see any of the 23 reviews, on the one screen where reviews exist to
inform a decision. The public
listReviewsendpoint has no caller in thefrontend at all —
CourseReviewsis the only consumer and it is behind acompletion gate.
Most enrolled students cannot write one. The server's rule is enrolment;
the UI's rule is 100% completion. A student who has watched nine of ten
sections is authorised by the API and has no button. Reviews are therefore
collected only from the subset who finish courses, which biases every average
on the catalogue upward — and the bias is invisible because the count looks
like it covers all enrolments.
Steps to reproduce
star rating.
there is no course detail page to navigate to.
GET /api/reviews/<courseId>in a terminal returns the reviews the UI willnot show you.
Expected result
Reviews are readable wherever a rating is shown, without an account. Any
enrolled student can leave, edit and delete a review from the course player,
matching the rule the API enforces.
Actual result
Reviews are write-once-at-100%-completion and readable by nobody. A public
endpoint has no consumer, and a badge advertises data that has no route to the
screen.
Suggested fix
Move
<CourseReviews>out of the certificate modal and onto the course playeritself, where any enrolled student reaches it. Make the catalogue's rating
badge open the reviews for that course — the summaries are already batched by
useRatingSummaries, so the list is one request on demand and nothing on thecatalogue's critical path. Leave the certificate modal's congratulation
intact; it is a reasonable prompt, just not the only door.