1- import React , { useEffect , useState } from 'react' ;
1+ import React , { useEffect , useState , useMemo } from 'react' ;
22import Cookies from 'js-cookie' ;
33import Notiflix from 'notiflix' ;
44import { Modal } from '../Modal.js' ;
55import SearchBar from '../search-bar.js'
6+ import CourseCard from '../CourseCard.js' ;
67
78export function CourseDashboard ( ) {
8- const [ courseInfo , setCourseInfo ] = useState ( null ) ;
9- const [ userInfo , setUserInfo ] = useState ( null ) ;
10- const [ newCourse , setNewCourse ] = useState ( {
11- title : "" ,
12- description : "" ,
13- } ) ;
9+ const userId = Cookies . get ( 'userId' ) ;
10+ const [ courseInfo , setCourseInfo ] = useState ( [ ] ) ;
11+ const [ userInfo , setUserInfo ] = useState ( null ) ;
12+ const [ newCourse , setNewCourse ] = useState ( { title : "" , description : "" } ) ;
1413 const [ searchQuery , setSearchQuery ] = useState ( "" ) ;
1514 const [ error , setError ] = useState ( null ) ;
1615 const [ courses , setCourses ] = useState ( [ ] ) ;
@@ -29,20 +28,12 @@ export function CourseDashboard() {
2928 if ( newCourse . title === "" || newCourse . description === "" ) {
3029 return ;
3130 }
32-
33- const userId = Cookies . get ( 'userId' ) ;
3431 try {
3532 const response = await fetch ( `http://localhost:4000/create-course/${ userId } ` , {
3633 method : "POST" ,
37- headers : {
38- "Content-Type" : "application/json" ,
39- } ,
40- body : JSON . stringify ( {
41- title : newCourse . title ,
42- description : newCourse . description ,
43- } ) ,
34+ headers : { "Content-Type" : "application/json" } ,
35+ body : JSON . stringify ( newCourse ) ,
4436 } ) ;
45-
4637 const data = await response . json ( ) ;
4738 if ( response . ok ) {
4839 Notiflix . Notify . success ( "Course creation successful!" ) ;
@@ -52,64 +43,52 @@ export function CourseDashboard() {
5243 } else {
5344 Notiflix . Notify . failure ( data . message || "Course creation failed" ) ;
5445 }
55- } catch ( error ) {
46+ } catch {
5647 Notiflix . Notify . failure ( "Error occurred during course creation" ) ;
5748 }
5849 } ;
5950
6051 useEffect ( ( ) => {
61- const userId = Cookies . get ( 'userId' ) ;
62-
6352 if ( ! userId ) {
64- setError ( ' User ID not found' ) ;
53+ setError ( " User ID not found" ) ;
6554 return ;
6655 }
67-
68- async function fetchCourses ( ) {
69- await fetch ( `http://localhost:4000/courses/${ userId } ` )
70- . then ( ( response ) => {
71- if ( ! response . ok ) {
72- throw new Error ( 'Network response was not ok' ) ;
73- }
74- return response . json ( ) ;
75- } )
76- . then ( ( data ) => setCourseInfo ( data . courses ) )
77- . catch ( ( error ) => setError ( error . message ) ) ;
78- }
79-
80- async function fetchUser ( ) {
81- await fetch ( `http://localhost:4000/user/${ userId } ` )
82- . then ( ( response ) => {
83- if ( ! response . ok ) {
84- throw new Error ( 'Network response was not ok' ) ;
85- }
86- return response . json ( ) ;
87- } )
88- . then ( ( data ) => setUserInfo ( data . user ) )
89- . catch ( ( error ) => setError ( error . message ) ) ;
90- }
91-
92- async function fetchAllCourses ( ) {
56+ const fetchAllData = async ( ) => {
9357 try {
94- const response = await fetch ( 'http://localhost:4000/course/' ) ;
95- if ( ! response . ok ) {
96- throw new Error ( 'Network response was not ok' ) ;
58+ const [ coursesRes , userRes , allCoursesRes ] = await Promise . all ( [
59+ fetch ( `http://localhost:4000/courses/${ userId } ` ) ,
60+ fetch ( `http://localhost:4000/user/${ userId } ` ) ,
61+ fetch ( "http://localhost:4000/course/" ) ,
62+ ] ) ;
63+ if ( ! coursesRes . ok || ! userRes . ok || ! allCoursesRes . ok ) {
64+ throw new Error ( "Failed to fetch data" ) ;
9765 }
98- const data = await response . json ( ) ;
99- setCourses ( data . courses || [ ] ) ;
100- setLoading ( false ) ;
101- } catch ( error ) {
66+ const [ coursesData , userData , allCoursesData ] = await Promise . all ( [
67+ coursesRes . json ( ) ,
68+ userRes . json ( ) ,
69+ allCoursesRes . json ( ) ,
70+ ] ) ;
71+ setCourseInfo ( coursesData . courses || [ ] ) ;
72+ setUserInfo ( userData . user || null ) ;
73+ setCourses ( allCoursesData . courses || [ ] ) ;
74+ } catch ( err ) {
75+ setError ( err . message ) ;
76+ } finally {
10277 setLoading ( false ) ;
10378 }
104- }
105- fetchAllCourses ( ) ;
79+ } ;
80+ fetchAllData ( ) ;
81+ } , [ userId ] ) ;
10682
107- fetchCourses ( ) ;
108- fetchUser ( ) ;
109- } , [ ] ) ;
83+ const filteredCourses = useMemo ( ( ) => {
84+ return courses . filter ( ( course ) =>
85+ course . title . toLowerCase ( ) . includes ( searchQuery . toLowerCase ( ) )
86+ ) ;
87+ } , [ courses , searchQuery ] ) ;
11088
89+ if ( loading ) return < p > Loading...</ p > ;
11190 if ( error ) return < p > Error: { error } </ p > ;
112- if ( ! courseInfo || ! userInfo ) return < p > Loading... </ p > ;
91+
11392
11493 let createButton = null ;
11594 if ( userInfo . role === "educator" ) {
@@ -134,47 +113,13 @@ export function CourseDashboard() {
134113 ) ;
135114 }
136115
137- const filteredCourses = courses . filter ( ( course ) =>
138- course . title . toLowerCase ( ) . includes ( searchQuery . toLowerCase ( ) )
139- ) ;
140-
141116 const myCourses = courseInfo . map ( ( course ) => (
142- < a href = { `/courses/${ course . ID } ` } key = { course . ID } className = "max-w-[40vw] aspect-[9/8]" >
143- < div className = "bg-gray-100 rounded shadow hover:bg-gray-300 flex flex-col h-full w-full outline outline-1 outline-black/25" >
144- < div className = "h-[60%] " >
145- < img
146- className = "rounded outline outline-1 outline-black/40 object-fill w-full h-full block aspect-[16/9]"
147- src = { process . env . PUBLIC_URL + `/content/${ course . ID } /thumbnail.png` }
148- onError = { ( e ) => { e . target . onError = null ; e . target . src = `/default_thumbnails/tn${ course . ID % 5 } .png` } }
149- alt = "Thumbnail"
150- />
151- </ div >
152- < div className = "h-[40%] p-2 md:p-4 xl:p-2 overflow-hidden" >
153- < h3 className = "text-base font-semibold truncate overflow-hidden" > { course . title } </ h3 >
154- < p className = "truncate overflow-hidden" > { course . description } </ p >
155- </ div >
156- </ div >
157- </ a >
117+ < CourseCard key = { course . ID } course = { course } />
158118 ) ) ;
159119
160120 const allCourses = filteredCourses . map ( ( course ) => (
161- < a href = { `/courses/${ course . ID } ` } key = { course . ID } className = "max-w-[40vw] aspect-[9/8]" >
162- < div className = "bg-gray-100 rounded shadow hover:bg-gray-300 flex flex-col h-full w-full outline outline-1 outline-black/25" >
163- < div className = "h-[60%] " >
164- < img
165- className = "rounded outline outline-1 outline-black/40 object-fill w-full h-full block aspect-[16/9]"
166- src = { process . env . PUBLIC_URL + `/content/${ course . ID } /thumbnail.png` }
167- onError = { ( e ) => { e . target . onError = null ; e . target . src = `/default_thumbnails/tn${ course . ID % 5 } .png` } }
168- alt = "Thumbnail"
169- />
170- </ div >
171- < div className = "h-[40%] pl-2 p-1 overflow-hidden" >
172- < h3 className = "text-base font-semibold truncate overflow-hidden" > { course . title } </ h3 >
173- < p className = "truncate overflow-hidden" > { course . description } </ p >
174- </ div >
175- </ div >
176- </ a >
177- ) )
121+ < CourseCard key = { course . ID } course = { course } />
122+ ) ) ;
178123
179124 return (
180125 < div className = "p-6" >
@@ -197,20 +142,21 @@ export function CourseDashboard() {
197142 </ div >
198143
199144 { /* All Courses Section */ }
200- < div className = "mt-10" >
201- < h2 className = "text-xl font-semibold mb-4" > All Courses</ h2 >
202- < div className = "grid grid-cols-2 sm:grid-cols-2 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-8 gap-7" >
203- { loading ? (
204- < p > Loading courses...</ p >
205- ) : filteredCourses . length > 0 ? (
206- allCourses
207- ) : (
208- < div className = "course-box" >
209- < p > Nothing to show, yet</ p >
210- </ div >
211- ) }
212- </ div >
213- </ div >
145+ { userInfo . role !== "educator" && (
146+ < div className = "mt-10" >
147+ < h2 className = "text-xl font-semibold mb-4" > All Courses</ h2 >
148+ < div className = "grid grid-cols-2 sm:grid-cols-2 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-8 gap-7" >
149+ { loading ? (
150+ < p > Loading courses...</ p >
151+ ) : filteredCourses . length > 0 ? (
152+ allCourses
153+ ) : (
154+ < div className = "course-box" >
155+ < p > Nothing to show, yet</ p >
156+ </ div >
157+ ) }
158+ </ div >
159+ </ div > ) }
214160 </ div >
215161 ) ;
216162}
0 commit comments