Skip to content

Commit 6adce78

Browse files
committed
[Refactor] Refactor the course card disaply with filtering.
1 parent 4647846 commit 6adce78

File tree

1 file changed

+57
-111
lines changed

1 file changed

+57
-111
lines changed
Lines changed: 57 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
1-
import React, { useEffect, useState } from 'react';
1+
import React, { useEffect, useState, useMemo } from 'react';
22
import Cookies from 'js-cookie';
33
import Notiflix from 'notiflix';
44
import { Modal } from '../Modal.js';
55
import SearchBar from '../search-bar.js'
6+
import CourseCard from '../CourseCard.js';
67

78
export 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

Comments
 (0)