Skip to content

Commit

Permalink
Add review sorting (#316)
Browse files Browse the repository at this point in the history
3 basic options
* Most Recent (Default)
* Top Reviews
* Controversial

* Show verified reviews only checkbox
  • Loading branch information
js0mmer authored Oct 14, 2023
1 parent 04523cf commit 0c25e3a
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 11 deletions.
22 changes: 22 additions & 0 deletions site/src/component/Review/Review.scss
Original file line number Diff line number Diff line change
Expand Up @@ -181,4 +181,26 @@ $avatarWidth: 65px;
width: 8vh;
height: 8vh;
}
}

.sorting-menu {
margin-left: 0;
margin-right: 0;

> * {
margin-bottom: 15px;
}

.dropdown {
margin-right: 1vh;
}

#checkbox {
display: flex;
align-items: center;

label {
margin-bottom: 0.25rem;
}
}
}
53 changes: 45 additions & 8 deletions site/src/component/Review/Review.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,26 @@ import './Review.scss';
import { selectReviews, setReviews, setFormStatus } from '../../store/slices/reviewSlice';
import { useAppSelector, useAppDispatch } from '../../store/hooks';
import { CourseGQLData, ProfessorGQLData, ReviewData, VoteColorsRequest, VoteColor } from '../../types/types';
import { Checkbox, Dropdown } from 'semantic-ui-react';

export interface ReviewProps {
course?: CourseGQLData;
professor?: ProfessorGQLData;
}

enum SortingOption {
MOST_RECENT,
TOP_REVIEWS,
CONTROVERSIAL
}

const Review: FC<ReviewProps> = (props) => {
const dispatch = useAppDispatch();
const reviewData = useAppSelector(selectReviews);
const [voteColors, setVoteColors] = useState([]);
const openForm = useAppSelector(state => state.review.formOpen);
const [sortingOption, setSortingOption] = useState<SortingOption>(SortingOption.MOST_RECENT);
const [showOnlyVerifiedReviews, setShowOnlyVerifiedReviews] = useState(false);

const getColors = async (vote: VoteColorsRequest) => {
const res = await axios.patch('/api/reviews/getVoteColors', vote);
Expand All @@ -37,11 +46,6 @@ const Review: FC<ReviewProps> = (props) => {
})
.then(async (res: AxiosResponse<ReviewData[]>) => {
const data = res.data.filter((review) => review !== null);
data.sort((a, b) => {
let aScore = a.score + (a.verified ? 10000 : 0);
let bScore = b.score + (b.verified ? 10000 : 0);
return bScore - aScore;
})
let reviewIDs = [];
for(let i = 0;i<data.length;i++){
reviewIDs.push(data[i]._id);
Expand Down Expand Up @@ -89,6 +93,26 @@ const Review: FC<ReviewProps> = (props) => {
getReviews();
}, [props.course?.id, props.professor?.ucinetid]);

let sortedReviews: ReviewData[];
// filter verified if option is set
if (showOnlyVerifiedReviews) {
sortedReviews = reviewData.filter(review => review.verified);
} else { // if not, clone reviewData since its const
sortedReviews = reviewData.slice(0);
}

switch (sortingOption) {
case SortingOption.MOST_RECENT:
sortedReviews.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
break;
case SortingOption.TOP_REVIEWS: // the right side of || will fall back to most recent when score is equal
sortedReviews.sort((a, b) => b.score - a.score || new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
break;
case SortingOption.CONTROVERSIAL:
sortedReviews.sort((a, b) => a.score - b.score || new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
break;
}

const openReviewForm = () => {
dispatch(setFormStatus(true));
document.body.style.overflow = 'hidden';
Expand All @@ -104,9 +128,22 @@ const Review: FC<ReviewProps> = (props) => {
return (
<>
<div className='reviews'>
{reviewData.map((review, i) => {
if (review !== null) return (<SubReview review={review} key={i} course={props.course} professor={props.professor} colors={getU(review._id) as VoteColor} colorUpdater={updateVoteColors}/>)
})}
<div className='sorting-menu row'>
<Dropdown
placeholder='Sorting Option'
scrolling
selection
options={[{ text: 'Most Recent', value: SortingOption.MOST_RECENT },
{ text: 'Top Reviews', value: SortingOption.TOP_REVIEWS },
{ text: 'Controversial', value: SortingOption.CONTROVERSIAL }]}
value={sortingOption}
onChange={(e, s) => setSortingOption(s.value as SortingOption)}
/>
<div id="checkbox">
<Checkbox label="Show verified reviews only" checked={showOnlyVerifiedReviews} onChange={(e, props) => setShowOnlyVerifiedReviews(props.checked!)} />
</div>
</div>
{sortedReviews.map(review => <SubReview review={review} key={review._id} course={props.course} professor={props.professor} colors={getU(review._id) as VoteColor} colorUpdater={updateVoteColors}/>)}
<button type='button' className='add-review-btn' onClick={openReviewForm}>+ Add Review</button>
</div>
<ReviewForm closeForm={closeForm} {...props} />
Expand Down
4 changes: 2 additions & 2 deletions site/src/component/Review/SubReview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,8 @@ const SubReview: FC<SubReviewProps> = ({ review, course, professor, colors, colo
</div>
</div>
<div>
{review.tags?.map((tag, i) =>
<Badge pill className='p-3 mr-2 mt-2' variant='info' key={`review-tag-${review._id}-${i}`}>
{review.tags?.map(tag =>
<Badge pill className='p-3 mr-2 mt-2' variant='info' key={tag}>
{tag}
</Badge>
)}
Expand Down
2 changes: 1 addition & 1 deletion site/src/component/ReviewForm/ReviewForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@ const ReviewForm: FC<ReviewFormProps> = (props) => {
<Form.Label>Select up to 3 tags</Form.Label>
<div>
{tags.map((tag, i) =>
<Badge pill className='p-3 mr-2 mt-2' variant={selectedTags.includes(tag) ? 'success' : 'info'} id={`tag-${i}`}
<Badge key={tag} pill className='p-3 mr-2 mt-2' variant={selectedTags.includes(tag) ? 'success' : 'info'} id={`tag-${i}`}
onClick={(e: React.MouseEvent<HTMLInputElement>) => { selectTag(tag) }}>
{tag}
</Badge>
Expand Down

0 comments on commit 0c25e3a

Please sign in to comment.