diff --git a/controllers/contentControllers/exercise.js b/controllers/contentControllers/exercise.js index 8c4c4a8..4b75c7e 100644 --- a/controllers/contentControllers/exercise.js +++ b/controllers/contentControllers/exercise.js @@ -532,7 +532,7 @@ export const createExercises = async (req, res) => { }); if (existingExercise) { throw new Error( - `An exercise with the title ${generatedTitle} already exists` + `An exercise with the TITLE ${generatedTitle} already exists` ); } @@ -854,6 +854,76 @@ export const updateExerciseById = async (req, res) => { } }; +export const updateExerciseTitle = async (req, res) => { + const { ID_LEVEL, exercises } = req.body; + const transaction = await models.db.transaction(); + + try { + const existingExercises = await models.Exercise.findAll({ + where: { + IS_DELETED: 0, + ID_LEVEL, + }, + order: [["TITLE", "ASC"]], + transaction, + }); + + if (exercises.length !== existingExercises.length) { + await transaction.rollback(); + return response(400, null, "Mismatch in exercise count", res); + } + + const sortedTitles = existingExercises.map((ex) => ex.TITLE); + const updatedExercises = []; + + for (let i = 0; i < exercises.length; i++) { + const { ID_ADMIN_EXERCISE } = exercises[i]; + const newTitle = sortedTitles[i]; + + const exerciseToUpdate = await models.Exercise.findByPk( + ID_ADMIN_EXERCISE, + { + transaction, + } + ); + + if (!exerciseToUpdate) { + await transaction.rollback(); + return response( + 404, + null, + `Exercise with ID ${ID_ADMIN_EXERCISE} not found`, + res + ); + } + + if (exerciseToUpdate.TITLE !== newTitle) { + await exerciseToUpdate.update({ TITLE: newTitle }, { transaction }); + + updatedExercises.push({ + ID_ADMIN_EXERCISE: exerciseToUpdate.ID_ADMIN_EXERCISE, + TITLE: newTitle, + oldTitle: exercises[i].TITLE, + }); + } + } + + await transaction.commit(); + + response( + 200, + { updatedExercises }, + "Exercises titles swapped successfully", + res + ); + } catch (error) { + console.error(error); + + await transaction.rollback(); + response(500, null, "Internal Server Error", res); + } +}; + export const deleteExerciseById = async (req, res) => { const { id } = req.params; const transaction = await models.db.transaction(); diff --git a/controllers/contentControllers/topic.js b/controllers/contentControllers/topic.js index 141966f..a7d08d3 100644 --- a/controllers/contentControllers/topic.js +++ b/controllers/contentControllers/topic.js @@ -179,7 +179,7 @@ export const createTopic = async (req, res) => { return response(400, null, "Topic description is required", res); } - const transaction = await models.sequelize.transaction(); + const transaction = await models.db.transaction(); try { const section = await models.Section.findByPk(ID_SECTION); @@ -188,6 +188,24 @@ export const createTopic = async (req, res) => { return response(404, null, "Section not found", res); } + const existingTopic = await models.Topic.findOne({ + where: { + ID_SECTION, + NAME_TOPIC, + IS_DELETED: 0, + }, + }); + + if (existingTopic) { + await transaction.rollback(); + return response( + 409, + null, + "Topic with the same name already exists", + res + ); + } + const newTopic = await models.Topic.create( { ID_SECTION, @@ -303,7 +321,6 @@ export const getCompletedTopicsBySection = async (req, res) => { const user = req.user; const userId = user.ID; - // Ambil semua pembelajaran user const userLearnings = await models.StdLearning.findAll({ where: { ID: userId }, include: [ @@ -344,12 +361,10 @@ export const getCompletedTopicsBySection = async (req, res) => { const { levelTopic: topic } = level; const { topicSection: section } = topic; - // Ambil total topik untuk setiap section const totalTopicsInSection = await models.Topic.count({ where: { ID_SECTION: section.ID_SECTION }, }); - // Jika section belum ada di objek completedSections, tambahkan if (!completedSections[section.ID_SECTION]) { completedSections[section.ID_SECTION] = { ID_SECTION: section.ID_SECTION, @@ -357,11 +372,10 @@ export const getCompletedTopicsBySection = async (req, res) => { DESCRIPTION_SECTION: section.DESCRIPTION_SECTION, THUMBNAIL: section.THUMBNAIL, TOTAL_TOPICS: totalTopicsInSection, - COMPLETED_TOPICS: 0, // Awal dengan 0 + COMPLETED_TOPICS: 0, }; } - // Cek apakah level saat ini adalah Level 6 untuk topik tersebut const level6 = await models.Level.findOne({ where: { NAME_LEVEL: "Level 6", @@ -369,22 +383,28 @@ export const getCompletedTopicsBySection = async (req, res) => { }, }); - // Jika Level 6 ditemukan dan IS_PASS bernilai 1, tambahkan COMPLETED_TOPICS - if (level6 && level.ID_LEVEL === level6.ID_LEVEL && learning.IS_PASS === 1) { + if ( + level6 && + level.ID_LEVEL === level6.ID_LEVEL && + learning.IS_PASS === 1 + ) { completedSections[section.ID_SECTION].COMPLETED_TOPICS++; } } - // Siapkan hasil akhir dengan section yang selalu muncul, meski tidak ada COMPLETED_TOPICS const result = Object.values(completedSections).map((section) => { - section.COMPLETED_TOPICS = section.COMPLETED_TOPICS || 0; // Tetap 0 jika tidak ada yang pass + section.COMPLETED_TOPICS = section.COMPLETED_TOPICS || 0; return section; }); - response(200, result, "Completed topics by section fetched successfully", res); + response( + 200, + result, + "Completed topics by section fetched successfully", + res + ); } catch (error) { console.error(error); response(500, null, "Internal Server Error", res); } }; - diff --git a/middlewares/Level/checkLevel.js b/middlewares/Level/checkLevel.js index df8b74b..eb8d601 100644 --- a/middlewares/Level/checkLevel.js +++ b/middlewares/Level/checkLevel.js @@ -391,7 +391,6 @@ export const autoGenerateLevelUpdateOtherRoutes = async (req, res, createdLevels } }; - export const updateOtherLevelsRoutesOnDelete = async (req, res, next) => { const { newLevelId } = req.body; diff --git a/routes/contents/exercise.js b/routes/contents/exercise.js index 97274f2..4e6b638 100644 --- a/routes/contents/exercise.js +++ b/routes/contents/exercise.js @@ -1,11 +1,11 @@ import express from "express"; -import { getExercises, getExerciseById, getExercisesForAdmin, getExerciseByLevelId, getExerciseByLevelIdForAdmin, createExercises, updateExerciseById, deleteExerciseById, deleteExerciseFileById } from "../../controllers/contentControllers/exercise.js"; -import { createMultipleChoicesExercise, updateMultipleChoicesExerciseById } from "../../controllers/exerciseTypesControllers/multipleChoices.js"; -import { createMatchingPairsExercise, updateMatchingPairsExerciseById } from "../../controllers/exerciseTypesControllers/matchingPairs.js"; -import { createTrueFalseExercise, updateTrueFalseExerciseById } from "../../controllers/exerciseTypesControllers/trueFalse.js"; +import { getExercises, getExerciseById, getExercisesForAdmin, getExerciseByLevelId, getExerciseByLevelIdForAdmin, createExercises, updateExerciseById, updateExerciseTitle, deleteExerciseById, deleteExerciseFileById } from "../../controllers/contentControllers/exercise.js"; +// import { createMultipleChoicesExercise, updateMultipleChoicesExerciseById } from "../../controllers/exerciseTypesControllers/multipleChoices.js"; +// import { createMatchingPairsExercise, updateMatchingPairsExerciseById } from "../../controllers/exerciseTypesControllers/matchingPairs.js"; +// import { createTrueFalseExercise, updateTrueFalseExerciseById } from "../../controllers/exerciseTypesControllers/trueFalse.js"; import { verifyLoginUser, adminOnly } from "../../middlewares/User/authUser.js"; import handleUpload from '../../middlewares/uploadExercise.js'; -import handleUploadSingleExercise from '../../middlewares/uploadExerciseSingle.js'; +// import handleUploadSingleExercise from '../../middlewares/uploadExerciseSingle.js'; const router = express.Router(); @@ -22,19 +22,21 @@ router.get("/exercise/:id", verifyLoginUser, getExerciseById); router.post("/exercises", verifyLoginUser, adminOnly, handleUpload, createExercises); -router.post("/exercise/multiple-choices", verifyLoginUser, adminOnly, handleUploadSingleExercise, createMultipleChoicesExercise); +// router.post("/exercise/multiple-choices", verifyLoginUser, adminOnly, handleUploadSingleExercise, createMultipleChoicesExercise); -router.post("/exercise/matching-pairs", verifyLoginUser, adminOnly, handleUploadSingleExercise, createMatchingPairsExercise); +// router.post("/exercise/matching-pairs", verifyLoginUser, adminOnly, handleUploadSingleExercise, createMatchingPairsExercise); -router.post("/exercise/true-false", verifyLoginUser, adminOnly, handleUploadSingleExercise, createTrueFalseExercise); +// router.post("/exercise/true-false", verifyLoginUser, adminOnly, handleUploadSingleExercise, createTrueFalseExercise); + +router.put("/exercise/title", verifyLoginUser, adminOnly, updateExerciseTitle); router.put("/exercise/:id", verifyLoginUser, adminOnly, handleUpload, updateExerciseById); -router.put("/exercise/multiple-choices/:id", verifyLoginUser, adminOnly, handleUploadSingleExercise, updateMultipleChoicesExerciseById); +// router.put("/exercise/multiple-choices/:id", verifyLoginUser, adminOnly, handleUploadSingleExercise, updateMultipleChoicesExerciseById); -router.put("/exercise/matching-pairs/:id", verifyLoginUser, adminOnly, handleUploadSingleExercise, updateMatchingPairsExerciseById); +// router.put("/exercise/matching-pairs/:id", verifyLoginUser, adminOnly, handleUploadSingleExercise, updateMatchingPairsExerciseById); -router.put("/exercise/true-false/:id", verifyLoginUser, adminOnly, handleUploadSingleExercise, updateTrueFalseExerciseById); +// router.put("/exercise/true-false/:id", verifyLoginUser, adminOnly, handleUploadSingleExercise, updateTrueFalseExerciseById); router.delete("/exercise/:id", verifyLoginUser, adminOnly, deleteExerciseById);