From 49592ac660ab788dcdc0211d467fa8220f7e0af1 Mon Sep 17 00:00:00 2001 From: Jacob Sommer Date: Wed, 13 Nov 2024 15:22:26 -0800 Subject: [PATCH] Do duplicate review check on frontend (#501) * frontend check for duplicate review in form * remove explicit dup check on backend --- api/src/controllers/reviews.ts | 12 ---------- site/src/component/ReviewForm/ReviewForm.tsx | 25 +++++++++++++++++--- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/api/src/controllers/reviews.ts b/api/src/controllers/reviews.ts index 66b4a54d..61b16217 100644 --- a/api/src/controllers/reviews.ts +++ b/api/src/controllers/reviews.ts @@ -125,18 +125,6 @@ const reviewsRouter = router({ verified: verifiedCount >= 3, // auto-verify if use has 3+ verified reviews }; - /** @todo: do a check for existing review on the frontend, remove this for the sake of speed since constraints will already prevent duplicate reviews on insertion */ - //check if review already exists for same professor, course, and user (do this before verifying captcha) - const existingReview = await db - .select({ count: count() }) - .from(review) - .where( - and(eq(review.userId, userId), eq(review.courseId, input.courseId), eq(review.professorId, input.professorId)), - ); - if (existingReview[0].count > 0) { - throw new TRPCError({ code: 'BAD_REQUEST', message: 'You have already reviewed this professor and course!' }); - } - // Verify the captcha const verifyResponse = await verifyCaptcha(reviewToAdd); if (!verifyResponse?.success) throw new TRPCError({ code: 'BAD_REQUEST', message: 'ReCAPTCHA token is invalid' }); diff --git a/site/src/component/ReviewForm/ReviewForm.tsx b/site/src/component/ReviewForm/ReviewForm.tsx index cb844bd5..715ad624 100644 --- a/site/src/component/ReviewForm/ReviewForm.tsx +++ b/site/src/component/ReviewForm/ReviewForm.tsx @@ -10,7 +10,7 @@ import RangeSlider from 'react-bootstrap-range-slider'; import Modal from 'react-bootstrap/Modal'; import ReCAPTCHA from 'react-google-recaptcha'; import { addReview, editReview } from '../../store/slices/reviewSlice'; -import { useAppDispatch } from '../../store/hooks'; +import { useAppDispatch, useAppSelector } from '../../store/hooks'; import { ReviewProps } from '../Review/Review'; import ThemeContext from '../../style/theme-context'; import { quarters } from '@peterportal/types'; @@ -63,6 +63,7 @@ const ReviewForm: FC = ({ const [anonymous, setAnonymous] = useState(reviewToEdit?.userDisplay === anonymousName); const [validated, setValidated] = useState(false); const { darkMode } = useContext(ThemeContext); + const reviews = useAppSelector((state) => state.review.reviews); useEffect(() => { if (show) { // form opened @@ -158,6 +159,12 @@ const ReviewForm: FC = ({ } }; + const alreadyReviewedCourseProf = (courseId: string, professorId: string) => { + return reviews.some( + (review) => review.courseId === courseId && review.professorId === professorId && review.authored, + ); + }; + // select instructor if in course context const instructorSelect = courseProp && ( @@ -176,8 +183,14 @@ const ReviewForm: FC = ({ {Object.keys(courseProp?.instructors).map((ucinetid) => { const name = courseProp?.instructors[ucinetid].name; + const alreadyReviewed = alreadyReviewedCourseProf(courseProp?.id, ucinetid); return ( - ); @@ -210,8 +223,14 @@ const ReviewForm: FC = ({ {Object.keys(professorProp?.courses).map((courseID) => { const name = professorProp?.courses[courseID].department + ' ' + professorProp?.courses[courseID].courseNumber; + const alreadyReviewed = alreadyReviewedCourseProf(courseID, professorProp?.ucinetid); return ( - );