diff --git a/backend/internal/handlers/courses.go b/backend/internal/handlers/courses.go index 4c999c7..0a12bfe 100644 --- a/backend/internal/handlers/courses.go +++ b/backend/internal/handlers/courses.go @@ -61,7 +61,7 @@ func Course(c *fiber.Ctx) error { Preload("Modules"). Preload("Modules.Content", func(db *gorm.DB) *gorm.DB { return db.Order("id ASC") - }). + }). Preload("Tags"). Where("id = ?", courseID). First(&course).Error; err != nil { @@ -196,6 +196,30 @@ func CreateCourse(c *fiber.Ctx) error { }) } +// Returns a true if user is enrolled in course +func IsEnrolled(c *fiber.Ctx) error { + course_id := c.Params("course_id") + user_id := c.Params("user_id") + + var count int64 + + if err := database.DB.Table("enrollment").Where("account_id = ? AND course_id = ?", user_id, course_id).Count(&count).Error; err != nil { + return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "Error retrieving courses"}) + } + + if count > 0 { + return c.JSON(fiber.Map{ + "message": "User is enrolled in this course", + "isEnrolled": true, + }) + } else { + return c.JSON(fiber.Map{ + "message": "User is not enrolled in this course", + "isEnrolled": false, + }) + } +} + // Enroll user into course func Enroll(c *fiber.Ctx) error { user_id := c.Params("user_id") @@ -223,6 +247,70 @@ func Enroll(c *fiber.Ctx) error { }) } +// Uneroll user into course +func Unenroll(c *fiber.Ctx) error { + user_id := c.Params("user_id") + course_id := c.Params("course_id") + + var user entity.Account + // Check if the user exists + if err := database.DB.Where("id = ?", user_id).First(&user).Error; err != nil { + return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "User not found"}) + } + + var course entity.Course + // Check if the course exists + if err := database.DB.Where("id = ?", course_id).First(&course).Error; err != nil { + return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "Course not found"}) + } + + // Check if the user is enrolled in the course + if err := database.DB.Model(&user).Association("Courses").Find(&course); err != nil { + return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "User is not enrolled in this course"}) + } + + // Unenroll user into course + if err := database.DB.Model(&user).Association("Courses").Delete(&course); err != nil { + return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "Error unenrolling into course"}) + } + + return c.JSON(fiber.Map{ + "message": fmt.Sprintf("Successfully unenrolled user id %d in course %s", user.ID, course.Title), + }) +} + +func CheckEnrollmentStatus(c *fiber.Ctx) error { + user_id := c.Params("user_id") + course_id := c.Params("course_id") + + var user entity.Account + // Check if the user exists + if err := database.DB.Where("id = ?", user_id).First(&user).Error; err != nil { + return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "User not found"}) + } + + var course entity.Course + // Check if the course exists + if err := database.DB.Where("id = ?", course_id).First(&course).Error; err != nil { + return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "Course not found"}) + } + + // Check if the user is enrolled in the course + isEnrolled := false + if err := database.DB.Table("enrollment"). + Where("account_id = ? AND course_id = ?", user_id, course_id). + Select("count(*) > 0"). + Scan(&isEnrolled).Error; err != nil { + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Error checking enrollment status"}) + } + + // Return the enrollment status + return c.JSON(fiber.Map{ + "message": "Enrollment status checked successfully", + "isEnrolled": isEnrolled, + }) +} + // Create a module inside a course func CreateModule(c *fiber.Ctx) error { creator_id, err := strconv.Atoi(c.Params("creator_id")) @@ -372,7 +460,7 @@ func EditContent(c *fiber.Ctx) error { if err := os.Remove("./content" + path); err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"message": err.Error()}) } - } else if !os.IsNotExist(err){ + } else if !os.IsNotExist(err) { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"message": err.Error()}) } @@ -480,7 +568,7 @@ func EditThumbnail(c *fiber.Ctx) error { if err := os.Remove(fmt.Sprintf("./content/%d/thumbnail.png", course_id)); err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"message": err.Error()}) } - } else if !os.IsNotExist(err){ + } else if !os.IsNotExist(err) { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"message": err.Error()}) } diff --git a/backend/internal/router/route.go b/backend/internal/router/route.go index 0a2da43..5d31828 100644 --- a/backend/internal/router/route.go +++ b/backend/internal/router/route.go @@ -91,5 +91,7 @@ func SetupRoutes(app *fiber.App) { app.Post("/delete-file/:creator_id/:content_id", handlers.DeleteFile) app.Post("/edit-content/:creator_id/:content_id", handlers.EditContent) app.Post("/edit-thumbnail/:creator_id/:course_id", handlers.EditThumbnail) + app.Get("/is-enrolled/:user_id/:course_id", handlers.IsEnrolled) app.Post("/enroll/:user_id/:course_id", handlers.Enroll) + app.Delete("/unenroll/:user_id/:course_id", handlers.Unenroll) } diff --git a/frontend/src/components/pages/Course.js b/frontend/src/components/pages/Course.js index 707d372..ca12198 100644 --- a/frontend/src/components/pages/Course.js +++ b/frontend/src/components/pages/Course.js @@ -10,6 +10,7 @@ export function Course() { const [userInfo, setUserInfo] = useState(null); const [error, setError] = useState(null); const [file, setFile] = useState(null); + const [isEnrolled, setIsEnrolled] = useState(null); const [newContentName, setNewContentName] = useState({ title: "", }); @@ -118,6 +119,67 @@ export function Course() { } }; + const handleEnroll = async () => { + const userId = Cookies.get('userId'); + + if (!userId) { + setError('User ID not found'); + return; + } + + try { + const response = await fetch(`http://localhost:4000/Enroll/${userId}/${courseID}`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + }); + const data = await response.json(); + if (response.ok) { + Notiflix.Notify.success("Succesfully enrolled in the course!"); + setTimeout(() => { + window.location.reload(); + }, 500); + } else { + Notiflix.Notify.failure(data.message || "Enrollment failed"); + } + } catch (error) { + Notiflix.Notify.failure("Error occurred during enrollment"); + } + + }; + + const handleUnenroll = async () => { + const userId = Cookies.get('userId'); + + if (!userId) { + setError('User ID not found'); + return; + } + + try { + const response = await fetch(`http://localhost:4000/Unenroll/${userId}/${courseID}`, { + method: "DELETE", + headers: { + "Content-Type": "application/json", + }, + }); + const data = await response.json(); + if (response.ok) { + Notiflix.Notify.success("Succesfully unenrolled in the course!"); + setTimeout(() => { + window.location.reload(); + }, 500); + } else { + Notiflix.Notify.failure(data.message || "Unenrollment failed"); + } + } catch (error) { + Notiflix.Notify.failure("Error occurred during Unenrollment"); + } + + }; + + useEffect(() => { const userId = Cookies.get('userId'); @@ -150,12 +212,25 @@ export function Course() { .catch((error) => setError(error.message)); } + async function fetchIsEnrolled() { + await fetch(`http://localhost:4000/is-enrolled/${userId}/${courseID}`) + .then((response) => { + if (!response.ok) { + throw new Error('Network response was not ok'); + } + return response.json(); + }) + .then((data) => setIsEnrolled(data.isEnrolled)) + .catch((error) => setError(error.message)); + } + fetchCourse(); fetchUser(); + fetchIsEnrolled(); }, [courseID]); if (error) return
Error: {error}
; - if (!courseInfo || !userInfo) returnLoading...
; + if (!courseInfo || !userInfo || isEnrolled === null) returnLoading...
; var moduleList = []; //Push all modules into moduleList @@ -248,6 +323,14 @@ export function Course() {