diff --git a/src/components/CourseFinder/CourseItem.jsx b/src/components/CourseFinder/CourseItem.jsx
index e82f09b1..25c44a76 100644
--- a/src/components/CourseFinder/CourseItem.jsx
+++ b/src/components/CourseFinder/CourseItem.jsx
@@ -36,7 +36,16 @@ const HighlightMatches = ({ content }) => {
}
const CourseItemMain = ({ courseData }) => {
- const { code, credits, department, title, description, tags } = courseData
+ const {
+ code,
+ credits,
+ department,
+ title,
+ description,
+ tags,
+ favoritedByCount,
+ } = courseData
+
const departmentList = useSelector(selectDepartments)
const colorPicker = useColorPicker()
@@ -80,7 +89,7 @@ const CourseItemMain = ({ courseData }) => {
))}
-
+
@@ -108,10 +117,15 @@ const CourseItemMain = ({ courseData }) => {
const CourseItemSub = ({ courseData }) => {
const { isMobile, isMobileS } = useResponsive()
- const { code, title, semester, reviews, resources } = courseData
-
- const reviewCount = reviews?.length
- const resourceCount = resources?.length
+ const {
+ code,
+ title,
+ semester,
+ reviewsCount,
+ resourcesCount,
+ reviewRequestersCount,
+ resourceRequestersCount,
+ } = courseData
return (
<>
@@ -129,14 +143,14 @@ const CourseItemSub = ({ courseData }) => {
style={{ width: '100%', borderRadius: '0.5rem 0 0 0.5rem' }}
>
- Reviews {reviewCount > 0 && `(${reviewCount})`}
+ Reviews {reviewsCount > 0 && `(${reviewsCount})`}
@@ -146,14 +160,14 @@ const CourseItemSub = ({ courseData }) => {
to={`${coursePageUrl(code, title)}#resources`}
>
- Resources {resourceCount > 0 && `(${resourceCount})`}
+ Resources {resourcesCount > 0 && `(${resourcesCount})`}
diff --git a/src/components/CoursePage/CourseContentRequest.jsx b/src/components/CoursePage/CourseContentRequest.jsx
index 16b21dc8..f550ab13 100644
--- a/src/components/CoursePage/CourseContentRequest.jsx
+++ b/src/components/CoursePage/CourseContentRequest.jsx
@@ -2,14 +2,15 @@ import { UserGroup } from '@styled-icons/heroicons-outline'
import { useState } from 'react'
import { useSelector } from 'react-redux'
-import { ButtonIcon, ButtonSwitch, toast } from 'components/shared'
+import { Badge, ButtonIcon, ButtonSwitch, toast } from 'components/shared'
import { API } from 'config/api'
import { selectUserProfile } from 'store/userSlice'
-export const useCourseContentRequest = ({ code, type }) => {
+export const useCourseContentRequest = ({ code, type, initialCount }) => {
const profile = useSelector(selectUserProfile)
const [loading, setLoading] = useState(false)
+ const [count, setCount] = useState(initialCount)
const [requestStatus, setRequestStatus] = useState(
profile[`${type}Requested`]?.includes(code) ?? false
)
@@ -19,8 +20,10 @@ export const useCourseContentRequest = ({ code, type }) => {
setLoading(true)
if (requestStatus) {
await API[type].request.remove({ code })
+ setCount(count - 1)
} else {
await API[type].request.add({ code })
+ setCount(count + 1)
}
setRequestStatus(!requestStatus)
@@ -35,6 +38,7 @@ export const useCourseContentRequest = ({ code, type }) => {
requestStatus,
handleRequest,
loading,
+ count,
}
}
@@ -59,21 +63,33 @@ export const CourseContentRequestSquare = ({ code, type, ...props }) => {
)
}
-export const CourseContentRequestIcon = ({ code, type, ...props }) => {
- const { requestStatus, handleRequest, loading } = useCourseContentRequest({
- code,
- type,
- })
+export const CourseContentRequestIcon = ({
+ code,
+ type,
+ initialCount,
+ ...props
+}) => {
+ const { requestStatus, handleRequest, loading, count } =
+ useCourseContentRequest({ code, type, initialCount })
return (
}
+ icon={
+ initialCount ? (
+
+
+
+ ) : (
+
+ )
+ }
$active={requestStatus}
onClick={handleRequest}
loading={loading}
+ style={{ width: '2.5rem', borderRadius: '0 0.5rem 0.5rem 0' }}
{...props}
/>
)
diff --git a/src/components/CoursePage/CoursePageBody.jsx b/src/components/CoursePage/CoursePageBody.jsx
index a90f871b..7d849672 100644
--- a/src/components/CoursePage/CoursePageBody.jsx
+++ b/src/components/CoursePage/CoursePageBody.jsx
@@ -8,15 +8,23 @@ import { device, fontSize } from 'styles/responsive'
import CourseWorkload from './CourseWorkload'
const CoursePageBody = ({ courseData }) => {
- const { code, title, department, credits, description, workload, semester } =
- courseData
+ const {
+ code,
+ title,
+ department,
+ credits,
+ description,
+ workload,
+ semester,
+ favoritedByCount,
+ } = courseData
return (
{code}
-
+
{title}
diff --git a/src/components/CourseResource/CourseResourceContainer.jsx b/src/components/CourseResource/CourseResourceContainer.jsx
index 20306b49..ced73160 100644
--- a/src/components/CourseResource/CourseResourceContainer.jsx
+++ b/src/components/CourseResource/CourseResourceContainer.jsx
@@ -3,15 +3,20 @@ import { useEffect, useState } from 'react'
import { useNavigate, useParams } from 'react-router-dom'
import styled from 'styled-components/macro'
-import { CourseContentRequest } from 'components/CoursePage'
+import {
+ CourseContentRequest,
+ CourseContentRequestIcon,
+} from 'components/CoursePage'
import { ButtonSquare, LoaderAnimation, toast } from 'components/shared'
import { API } from 'config/api'
+import { useResponsive } from 'hooks'
import { CourseResourceGrid } from './CourseResourceItem'
const CourseResourceContainer = () => {
const { code } = useParams()
const navigate = useNavigate()
+ const { isMobileS } = useResponsive()
const [resources, setResources] = useState([])
const [loading, setLoading] = useState(false)
@@ -42,11 +47,19 @@ const CourseResourceContainer = () => {
Resources
-
+ {isMobileS ? (
+
+ ) : (
+
+ )}
theme.textColor};
- font-weight: 400;
+ margin-top: 0.5rem;
+
+ a {
+ color: #239afc;
+
+ &:hover {
+ text-decoration: underline;
+ }
+ }
`
diff --git a/src/components/CourseReview/Editor.jsx b/src/components/CourseReview/Editor.jsx
index 92e1b32e..2c9a76d3 100644
--- a/src/components/CourseReview/Editor.jsx
+++ b/src/components/CourseReview/Editor.jsx
@@ -1,4 +1,3 @@
-import { DocumentText } from '@styled-icons/heroicons-outline'
import { useState } from 'react'
import ReactQuill from 'react-quill'
import { useSelector } from 'react-redux'
@@ -44,10 +43,14 @@ const modules = {
const CustomToolbar = ({ templateHandler }) => (
-
@@ -168,4 +171,9 @@ const Toolbar = styled.div`
select {
background: none;
}
+
+ button.ql-loadTemplate {
+ color: ${({ theme }) => theme.textColor};
+ width: fit-content;
+ }
`
diff --git a/src/components/Favourites/FavoriteToggle.jsx b/src/components/Favourites/FavoriteToggle.jsx
index f83c3c11..cf96c2f0 100644
--- a/src/components/Favourites/FavoriteToggle.jsx
+++ b/src/components/Favourites/FavoriteToggle.jsx
@@ -2,22 +2,26 @@ import { Bookmark, BookmarkOutline } from '@styled-icons/zondicons'
import { useState } from 'react'
import { useDispatch, useSelector } from 'react-redux'
-import { ButtonIcon, toast } from 'components/shared'
+import { Badge, ButtonIcon, toast } from 'components/shared'
import { API } from 'config/api'
import { selectFavouriteStatus, updateFavourite } from 'store/userSlice'
-const FavoriteToggle = ({ code }) => {
+const FavoriteToggle = ({ code, initialCount }) => {
const dispatch = useDispatch()
const favourite = useSelector(selectFavouriteStatus(code))
+
const [loading, setLoading] = useState(false)
+ const [count, setCount] = useState(initialCount)
const handleToggle = async () => {
try {
setLoading(true)
if (favourite) {
await API.courses.favorite.remove({ code })
+ setCount(count - 1)
} else {
await API.courses.favorite.add({ code })
+ setCount(count + 1)
}
dispatch(updateFavourite(code))
@@ -32,7 +36,11 @@ const FavoriteToggle = ({ code }) => {
: }
+ icon={
+
+ {favourite ? : }
+
+ }
color="white"
loading={loading}
/>
diff --git a/src/components/shared/Avatar.jsx b/src/components/shared/Avatar.jsx
index d28ae689..6bf59741 100644
--- a/src/components/shared/Avatar.jsx
+++ b/src/components/shared/Avatar.jsx
@@ -2,28 +2,40 @@ import { User } from '@styled-icons/heroicons-outline'
import { Avatar } from 'antd'
import styled from 'styled-components/macro'
+import { useResponsive } from 'hooks'
+import { useColorPicker } from 'styles/utils'
+
const StyledAvatar = styled(Avatar)`
display: flex;
align-items: center;
justify-content: center;
min-width: ${({ size }) => size};
min-height: ${({ size }) => size};
- border: 1px solid ${({ theme }) => theme.textColor};
+ color: ${({ theme }) => theme.darksecondary};
img {
- width: ${({ size }) => size};
- height: ${({ size }) => size};
+ width: 100%;
+ height: 100%;
}
`
-export const UserAvatar = ({ size = '2rem', src, alt = 'Profile picture' }) => {
- return src ? (
-
- ) : (
+export const UserAvatar = ({
+ size: initialSize,
+ src,
+ alt = 'Profile picture',
+}) => {
+ const colorPicker = useColorPicker()
+ const { isMobileS } = useResponsive()
+
+ const size = isMobileS ? '1rem' : initialSize ?? '2rem'
+
+ if (src) return
+
+ return (
}
- style={{ backgroundColor: '#87d068' }}
+ style={{ backgroundColor: colorPicker() }}
alt={alt}
/>
)
diff --git a/src/components/shared/Badge.jsx b/src/components/shared/Badge.jsx
new file mode 100644
index 00000000..6269916d
--- /dev/null
+++ b/src/components/shared/Badge.jsx
@@ -0,0 +1,13 @@
+import { Badge } from 'antd'
+import styled from 'styled-components/macro'
+
+const StyledBadge = styled(Badge)`
+ transform: scale(${({ scale }) => scale});
+ color: ${({ theme }) => theme.textColor};
+
+ .ant-badge-count {
+ box-shadow: none;
+ }
+`
+
+export default StyledBadge
diff --git a/src/components/shared/index.jsx b/src/components/shared/index.jsx
index ed8c8a7b..b608dba5 100644
--- a/src/components/shared/index.jsx
+++ b/src/components/shared/index.jsx
@@ -6,6 +6,7 @@ export {
ButtonDropdown,
ButtonSwitch,
} from './Buttons'
+export { default as Badge } from './Badge'
export { default as Card, CardSkeleton } from './Card'
export { default as CardSplit, CardSplitSkeleton } from './CardSplit'
export { default as Checkbox } from './Checkbox'
diff --git a/src/routes.jsx b/src/routes.jsx
index a3eab303..1167f60c 100644
--- a/src/routes.jsx
+++ b/src/routes.jsx
@@ -23,9 +23,9 @@ export const DashboardRoutes = () => (
} />
} />
- }>
-
-
+
+ } />
+ } />
} />
diff --git a/src/styles/utils.jsx b/src/styles/utils.jsx
index 9c12b3c7..6a5abaf0 100644
--- a/src/styles/utils.jsx
+++ b/src/styles/utils.jsx
@@ -1,4 +1,5 @@
import { adjustHue } from 'polished'
+import { useMemo } from 'react'
import { useSelector } from 'react-redux'
import { selectTheme } from 'store/settingsSlice'
@@ -54,11 +55,17 @@ export const useColorPicker = () => {
const theme = useSelector(selectTheme)
const paletteTheme = palette[theme]
+ const randomizeId = useMemo(
+ () => Math.floor(Math.random() * paletteTheme.length),
+ [paletteTheme.length]
+ )
+
if (!paletteTheme) {
throw new Error(`No palette theme found for ${theme}`)
}
- const colorPicker = (id) => paletteTheme[id % paletteTheme.length]
+ const colorPicker = (id = randomizeId) =>
+ paletteTheme[id % paletteTheme.length]
return colorPicker
}