11import React , { useEffect , useRef , useState } from 'react' ;
2+ import RatingComponent from "./RatingComponent.jsx" ;
3+ import { model } from "../../model.js" ;
24
35function CoursePagePopup ( {
4- favouriteCourses,
5- handleFavouriteClick,
6- isOpen,
7- onClose,
8- course,
9- prerequisiteTree,
10- reviewPresenter,
11- } ) {
6+ favouriteCourses,
7+ handleFavouriteClick,
8+ isOpen,
9+ onClose,
10+ course,
11+ prerequisiteTree,
12+ reviewPresenter,
13+ } ) {
14+
1215 const treeRef = useRef ( null ) ;
1316 const [ showOverlay , setShowOverlay ] = useState ( true ) ;
17+ const [ averageRating , setAverageRating ] = useState ( null ) ;
18+
19+
20+ useEffect ( ( ) => {
21+ const fetchAverageRating = async ( ) => {
22+ try {
23+ const avg = await model . getAverageRating ( course . code ) ;
24+ setAverageRating ( avg ) ;
25+ } catch ( error ) {
26+ setAverageRating ( null ) ;
27+ }
28+ } ;
29+
30+ if ( isOpen && course ) fetchAverageRating ( ) ;
31+
32+ } , [ isOpen , course ] ) ;
33+
1434
1535 useEffect ( ( ) => {
1636 const handleKeyDown = ( event ) => {
@@ -53,42 +73,74 @@ function CoursePagePopup({
5373 < h2 className = "text-5xl font-extrabold text-[#2e2e4f]" >
5474 < span className = "text-violet-700" > { course . code } </ span > - { course . name }
5575 < span className = "ml-4 text-lg text-violet-700 whitespace-nowrap" >
56- ({ course . credits } Credits)
57- </ span >
76+ ({ course . credits } Credits)
77+ </ span >
5878 </ h2 >
5979 < div className = "my-6 h-1.5 w-full bg-violet-500" > </ div >
6080 </ div >
61- < div >
81+ < div className = "flex justify-between items-center" >
6282 < button
6383 className = { `inline-flex items-center px-4 py-2 gap-2 rounded-lg
64- transition-all duration-300 ease-in-out
65- font-semibold text-sm shadow-sm
66- ${ favouriteCourses . some ( ( fav ) => fav . code === course . code )
67- ? 'bg-yellow-400 /90 hover:bg-yellow-500/90 border-2 border-yellow-600 hover:border-yellow-700 text-yellow-900'
68- : 'bg-yellow-200/90 hover:bg-yellow-300 border-2 border-yellow-400 hover:border-yellow-500 text-yellow-600 hover:text-yellow-700'
69- } `}
84+ transition-all duration-300 ease-in-out
85+ font-semibold text-sm shadow-sm
86+ ${
87+ favouriteCourses . some ( ( fav ) => fav . code === course . code )
88+ ? 'bg-yellow-400/90 hover:bg-yellow-500/90 border-2 border-yellow-600 hover:border-yellow-700 text-yellow-900'
89+ : 'bg-yellow-200/90 hover:bg-yellow-300 border-2 border-yellow-400 hover:border-yellow-500 text-yellow-600 hover:text-yellow-700'
90+ } `}
7091 onClick = { ( e ) => {
7192 e . stopPropagation ( ) ;
7293 handleFavouriteClick ( course ) ;
7394 } }
7495 >
7596 { favouriteCourses . some ( ( fav ) => fav . code === course . code ) ? (
7697 < >
77- < svg xmlns = "http://www.w3.org/2000/svg" className = "h-5 w-5 fill-yellow-900" viewBox = "0 0 20 20" >
78- < path d = "M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z" />
98+ < svg
99+ xmlns = "http://www.w3.org/2000/svg"
100+ className = "h-5 w-5 fill-yellow-900"
101+ viewBox = "0 0 20 20"
102+ >
103+ < path
104+ d = "M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z"
105+ />
79106 </ svg >
80107 Remove from Favourites
81108 </ >
82109 ) : (
83110 < >
84- < svg xmlns = "http://www.w3.org/2000/svg" className = "h-5 w-5 stroke-yellow-500" fill = "none" viewBox = "0 0 24 24" stroke = "currentColor" >
85- < path strokeLinecap = "round" strokeLinejoin = "round" strokeWidth = { 2 } d = "M11.049 2.927c.3-.921 1.603-.921 1.902 0l1.519 4.674a1 1 0 00.95.69h4.915c.969 0 1.371 1.24.588 1.81l-3.976 2.888a1 1 0 00-.363 1.118l1.518 4.674c.3.922-.755 1.688-1.538 1.118l-3.976-2.888a1 1 0 00-1.176 0l-3.976 2.888c-.783.57-1.838-.197-1.538-1.118l1.518-4.674a1 1 0 00-.363-1.118l-3.976-2.888c-.784-.57-.38-1.81.588-1.81h4.914a1 1 0 00.951-.69l1.519-4.674z" />
111+ < svg
112+ xmlns = "http://www.w3.org/2000/svg"
113+ className = "h-5 w-5 stroke-yellow-500"
114+ fill = "none"
115+ viewBox = "0 0 24 24"
116+ stroke = "currentColor"
117+ >
118+ < path
119+ strokeLinecap = "round"
120+ strokeLinejoin = "round"
121+ strokeWidth = { 2 }
122+ d = "M11.049 2.927c.3-.921 1.603-.921 1.902 0l1.519 4.674a1 1 0 00.95.69h4.915c.969 0 1.371 1.24.588 1.81l-3.976 2.888a1 1 0 00-.363 1.118l1.518 4.674c.3.922-.755 1.688-1.538 1.118l-3.976-2.888a1 1 0 00-1.176 0l-3.976 2.888c-.783.57-1.838-.197-1.538-1.118l1.518-4.674a1 1 0 00-.363-1.118l-3.976-2.888c-.784-.57-.38-1.81.588-1.81h4.914a1 1 0 00.951-.69l1.519-4.674z"
123+ />
86124 </ svg >
87125 Add to Favourites
88126 </ >
89127 ) }
90128 </ button >
129+
130+ < div className = "flex flex-col items-center" >
131+ { averageRating !== null ? (
132+ < p className = "text-lg font-semibold text-violet-700" >
133+ Average Rating: { averageRating } / 5
134+ </ p >
135+ ) : (
136+ < p className = "text-lg font-semibold text-violet-700" >
137+ No Reviews Yet
138+ </ p >
139+ ) }
140+ < RatingComponent readOnly = { true } value = { averageRating || 0 } />
141+ </ div >
91142 </ div >
143+
92144 { /* Description Section */ }
93145 < div >
94146 < h3 className = "text-2xl font-bold text-[#2e2e4f] mb-0.5" > Course Description</ h3 >
@@ -102,26 +154,25 @@ function CoursePagePopup({
102154 < div >
103155 < h3 className = "text-2xl font-semibold text-[#2e2e4f] mb-0.5" > Prerequisite Graph Tree</ h3 >
104156 < div className = "mb-4 h-0.5 w-full bg-violet-500 rounded-lg" > </ div >
105- < div className = "relative rounded-lg" >
106- { showOverlay && (
107- < div
108- className = "absolute inset-0 z-10 bg-indigo-200/10 rounded-lg cursor-pointer flex items-center justify-center z-51"
109- onClick = { ( e ) => {
110- e . stopPropagation ( ) ;
111- setShowOverlay ( false ) ;
112- } }
113- >
114- </ div >
115- ) }
157+ < div className = "relative rounded-lg" >
158+ { showOverlay && (
116159 < div
117- className = "bg-indigo-300/50 outline-none focus:outline-none focus:ring-2 focus:ring-violet-600 rounded-lg transition-shadow"
118- ref = { treeRef }
119- onClick = { handleTreeClick }
120- tabIndex = { 0 }
121- >
122- { prerequisiteTree }
123- </ div >
160+ className = "absolute inset-0 z-10 bg-indigo-200/10 rounded-lg cursor-pointer flex items-center justify-center z-51"
161+ onClick = { ( e ) => {
162+ e . stopPropagation ( ) ;
163+ setShowOverlay ( false ) ;
164+ } }
165+ > </ div >
166+ ) }
167+ < div
168+ className = "bg-indigo-300/50 outline-none focus:outline-none focus:ring-2 focus:ring-violet-600 rounded-lg transition-shadow"
169+ ref = { treeRef }
170+ onClick = { handleTreeClick }
171+ tabIndex = { 0 }
172+ >
173+ { prerequisiteTree }
124174 </ div >
175+ </ div >
125176 </ div >
126177 { /* Reviews Section (optional) */ }
127178 { reviewPresenter && (
0 commit comments