Skip to content

Commit e9b52b2

Browse files
committed
Fix dashboard formatting
1 parent 33a7453 commit e9b52b2

File tree

5 files changed

+144
-26
lines changed

5 files changed

+144
-26
lines changed

backend/internal/handlers/courses.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,3 +436,57 @@ func DeleteFile(c *fiber.Ctx) error {
436436
"message": "Successfully deleted file",
437437
})
438438
}
439+
440+
// Edit thumbnail of course
441+
func EditThumbnail(c *fiber.Ctx) error {
442+
creator_id, err := strconv.Atoi(c.Params("creator_id"))
443+
if err != nil {
444+
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid creator_id"})
445+
}
446+
447+
course_id, err := strconv.Atoi(c.Params("course_id"))
448+
if err != nil {
449+
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid course_id"})
450+
}
451+
452+
var course entity.Course
453+
// Check if the course exists
454+
if err := database.DB.Where("id = ?", course_id).First(&course).Error; err != nil {
455+
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "Course not found"})
456+
}
457+
458+
//Verify that the account attempting to create a module is the creator of the course
459+
if course.CreatorID != creator_id {
460+
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "User is not the course creator"})
461+
}
462+
463+
file, err := c.FormFile("file")
464+
if err != nil {
465+
if err.Error() != "there is no uploaded file associated with the given key" {
466+
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid file"})
467+
}
468+
}
469+
470+
if file != nil {
471+
if err := os.MkdirAll(fmt.Sprintf("./content/%d/", course_id), 0777); err != nil {
472+
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"message": err.Error()})
473+
}
474+
475+
//Remove previous attachment if there is one
476+
if _, err := os.Stat(fmt.Sprintf("./content/%d/thumbnail.png", course_id)); err == nil {
477+
if err := os.Remove(fmt.Sprintf("./content/%d/thumbnail.png", course_id)); err != nil {
478+
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"message": err.Error()})
479+
}
480+
} else if !os.IsNotExist(err){
481+
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"message": err.Error()})
482+
}
483+
484+
if err := c.SaveFile(file, fmt.Sprintf("./content/%d/thumbnail.png", course_id)); err != nil {
485+
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"message": err.Error()})
486+
}
487+
}
488+
489+
return c.JSON(fiber.Map{
490+
"message": "Successfully updated thumbnail",
491+
})
492+
}

backend/internal/router/route.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,5 +90,6 @@ func SetupRoutes(app *fiber.App) {
9090
app.Post("/create-content/:creator_id/:module_id", handlers.CreateContent)
9191
app.Post("/delete-file/:creator_id/:content_id", handlers.DeleteFile)
9292
app.Post("/edit-content/:creator_id/:content_id", handlers.EditContent)
93+
app.Post("/edit-thumbnail/:creator_id/:course_id", handlers.EditThumbnail)
9394
app.Post("/enroll/:user_id/:course_id", handlers.Enroll)
9495
}

frontend/src/components/Modal.js

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,26 +11,29 @@ export const Modal = ({ title, trigger, inputFields, changeHandler, confirmHandl
1111
setShowModal(true);
1212
};
1313

14-
const fieldList = Object.entries(inputFields).map(([name, text]) => (
15-
<div className="p-2" key={name}>
16-
<label className="block font-medium">{text + ":"}</label>
17-
{name === "body" ?
18-
<textarea
19-
className="border-2 border-gray-300 rounded p-2 size-11/12"
20-
type="text"
21-
name={name}
22-
onChange={changeHandler}
23-
/>:
24-
<input
25-
className="border-2 border-gray-300 rounded p-2 size-11/12"
26-
type="text"
27-
name={name}
28-
onChange={changeHandler}
29-
/>}
30-
31-
</div>
32-
));
33-
14+
var fieldList = null
15+
if (inputFields != null) {
16+
fieldList = Object.entries(inputFields).map(([name, text]) => (
17+
<div className="p-2" key={name}>
18+
<label className="block font-medium">{text + ":"}</label>
19+
{name === "body" ?
20+
<textarea
21+
className="border-2 border-gray-300 rounded p-2 size-11/12"
22+
type="text"
23+
name={name}
24+
onChange={changeHandler}
25+
/>:
26+
<input
27+
className="border-2 border-gray-300 rounded p-2 size-11/12"
28+
type="text"
29+
name={name}
30+
onChange={changeHandler}
31+
/>}
32+
33+
</div>
34+
));
35+
}
36+
3437
return (
3538
<div>
3639
<div onClick={open}>{trigger}</div>

frontend/src/components/pages/Course.js

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export function Course() {
99
const [courseInfo, setCourseInfo] = useState(null);
1010
const [userInfo, setUserInfo] = useState(null);
1111
const [error, setError] = useState(null);
12+
const [file, setFile] = useState(null);
1213
const [newContentName, setNewContentName] = useState({
1314
title: "",
1415
});
@@ -85,6 +86,38 @@ export function Course() {
8586
}
8687
};
8788

89+
const handleThumbnailUpload = (e) => {
90+
setFile(e.target.files[0]);
91+
}
92+
93+
const handleEditThumbnail = (courseID) => async (e) => {
94+
if (file === null){
95+
return
96+
}
97+
98+
const userId = Cookies.get('userId');
99+
try {
100+
const formData = new FormData();
101+
formData.append("file", file);
102+
const response = await fetch(`http://localhost:4000/edit-thumbnail/${userId}/${courseID}`, {
103+
method: "POST",
104+
body: formData,
105+
});
106+
107+
const data = await response.json();
108+
if (response.ok) {
109+
Notiflix.Notify.success("Thumbnail upload successful!");
110+
setTimeout(() => {
111+
window.location.reload();
112+
}, 500);
113+
} else {
114+
Notiflix.Notify.failure(data.message || "Thumbnail upload failed");
115+
}
116+
} catch (error) {
117+
Notiflix.Notify.failure("Error occurred during thumbnail upload");
118+
}
119+
};
120+
88121
useEffect(() => {
89122
const userId = Cookies.get('userId');
90123

@@ -194,11 +227,28 @@ export function Course() {
194227
/>
195228
}
196229

230+
let editThumbnail = null;
231+
if (userInfo.role === "educator"){
232+
editThumbnail = <Modal
233+
title={"Edit Thumbnail"}
234+
trigger={
235+
<div className="bg-blue-500 p-2 rounded shadow hover:bg-blue-700 m-auto text-center text-sm text-white font-semibold hover:cursor-pointer" >
236+
<div>Edit Thumbnail</div>
237+
</div>
238+
}
239+
confirmHandler={handleEditThumbnail(courseID)}
240+
fileUploadHandler={handleThumbnailUpload}
241+
/>
242+
}
243+
197244
return (
198245
<div className="p-6">
199246
<div className="flex justify-between flex-wrap mb-5">
200247
<h1 className="text-2xl font-bold mb-4">{courseInfo.title}</h1>
201-
{createButton}
248+
<div className="flex flex-1 justify-end flex-wrap gap-5 ml-20">
249+
{editThumbnail}
250+
{createButton}
251+
</div>
202252
</div>
203253
<div className="grid grid-cols-1 gap-6">
204254
{moduleList}

frontend/src/components/pages/CourseDashboard.js

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -139,10 +139,20 @@ export function CourseDashboard() {
139139
);
140140

141141
const myCourses = courseInfo.map((course) => (
142-
<a href={`/courses/${course.ID}`} key={course.ID}>
143-
<div className="bg-gray-100 p-4 rounded shadow hover:bg-gray-300">
144-
<h3 className="text-xl font-semibold truncate overflow-hidden">{course.title}</h3>
145-
<p className="mt-2">{course.description}</p>
142+
<a href={`/courses/${course.ID}`} key={course.ID} className="max-w-[40vw]">
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.className = "invisible"}}
149+
alt="Thumbnail"
150+
/>
151+
</div>
152+
<div className="p-4">
153+
<h3 className="text-xl font-semibold truncate overflow-hidden">{course.title}</h3>
154+
<p className="mt-2 truncate overflow-hidden">{course.description}</p>
155+
</div>
146156
</div>
147157
</a>
148158
));
@@ -171,7 +181,7 @@ export function CourseDashboard() {
171181
{/* My Courses Section */}
172182
<div className="mt-10">
173183
<h2 className="text-xl font-semibold mb-4">My Courses</h2>
174-
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
184+
<div className="grid grid-cols-2 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-6 gap-9">
175185
{myCourses}
176186
</div>
177187
</div>

0 commit comments

Comments
 (0)