From 34f58fc6bbe8ed73a7b89cac944dd176f8b95236 Mon Sep 17 00:00:00 2001 From: Jacob Sommer Date: Wed, 6 Nov 2024 13:07:54 -0800 Subject: [PATCH 1/2] frontend check for duplicate review in form --- site/src/component/ReviewForm/ReviewForm.tsx | 25 +++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/site/src/component/ReviewForm/ReviewForm.tsx b/site/src/component/ReviewForm/ReviewForm.tsx index 19fe3d9d..9afcc5b2 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 { quarterNames } 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].shortenedName; + 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 ( - ); From 402610d39a22fefbf7aa5a1e19a8a0908170bdf7 Mon Sep 17 00:00:00 2001 From: Jacob Sommer Date: Wed, 6 Nov 2024 13:08:45 -0800 Subject: [PATCH 2/2] remove explicit dup check on backend --- api/src/controllers/reviews.ts | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/api/src/controllers/reviews.ts b/api/src/controllers/reviews.ts index 58350d9f..6f66c8fa 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' });