import response from "../../response.js"; import models from "../../models/index.js"; import { autoGenerateLevel } from "../../middlewares/Level/autoGenerateLevel.js"; export const getTopics = async (req, res) => { try { const topics = await models.Topic.findAll({ where: { IS_DELETED: 0, }, }); response(200, topics, "Success", res); } catch (error) { console.log(error); response(500, null, "Error retrieving topics data!", res); } }; export const getTopicById = async (req, res) => { try { const { id } = req.params; const topic = await models.Topic.findByPk(id); if (!topic) { return response(404, null, "Topic not found", res); } response(200, topic, "Success", res); } catch (error) { console.log(error); response(500, null, "Internal Server Error", res); } }; export const getTopicBySectionId = async (req, res) => { try { const { sectionId } = req.params; const userId = req.user.ID; const sectionExists = await models.Section.findByPk(sectionId); if (!sectionExists) { return response(404, null, "Section not found", res); } const topics = await models.Topic.findAll({ where: { ID_SECTION: sectionId, IS_DELETED: 0 }, attributes: ["ID_TOPIC", "NAME_TOPIC", "DESCRIPTION_TOPIC"], }); if (!topics || topics.length === 0) { return response(404, null, "No topics found for this section", res); } const topicsWithCompletionStatus = await Promise.all( topics.map(async (topic) => { const level6 = await models.Level.findOne({ where: { NAME_LEVEL: "Level 6", ID_TOPIC: topic.ID_TOPIC, }, }); let isCompleted = 0; if (level6) { const stdLearning = await models.StdLearning.findOne({ where: { ID: userId, ID_LEVEL: level6.ID_LEVEL, IS_PASS: 1, }, }); isCompleted = stdLearning ? 1 : 0; } return { ...topic.get({ plain: true }), IS_COMPLETED: isCompleted, }; }) ); response(200, topicsWithCompletionStatus, "Success", res); } catch (error) { console.error(error); response(500, null, "Internal Server Error", res); } }; export const getTopicForAdmin = async (req, res) => { const { page = 1, limit = 10, search = "", sort = "time" } = req.query; try { const { count, rows: topics } = await models.Topic.findAndCountAll({ where: { IS_DELETED: 0, ...(search && { [models.Op.or]: [ { "$topicSection.NAME_SECTION$": { [models.Op.like]: `%${search}%`, }, }, { NAME_TOPIC: { [models.Op.like]: `%${search}%`, }, }, ], }), }, attributes: ["ID_TOPIC", "NAME_TOPIC", "TIME_TOPIC"], include: [ { model: models.Section, as: "topicSection", attributes: ["ID_SECTION", "NAME_SECTION"], }, ], distinct: true, }); const formattedTopics = topics.map((topic) => ({ ID_SECTION: topic.topicSection.ID_SECTION, ID_TOPIC: topic.ID_TOPIC, NAME_SECTION: topic.topicSection.NAME_SECTION, NAME_TOPIC: topic.NAME_TOPIC, TIME_TOPIC: topic.TIME_TOPIC, })); if (sort === "section") { formattedTopics.sort((a, b) => a.NAME_SECTION.localeCompare(b.NAME_SECTION) ); } else if (sort === "topic") { formattedTopics.sort((a, b) => a.NAME_TOPIC.localeCompare(b.NAME_TOPIC)); } else { formattedTopics.sort( (a, b) => new Date(b.TIME_TOPIC) - new Date(a.TIME_TOPIC) ); } const paginatedTopics = formattedTopics.slice( (page - 1) * limit, page * limit ); const totalPages = Math.ceil(count / limit); const currentPage = parseInt(page); response( 200, { topics: paginatedTopics, currentPage, totalPages, totalItems: count, }, "Topics retrieved successfully", res ); } catch (error) { console.log(error); response(500, null, "Error retrieving topics data!", res); } }; export const createTopic = async (req, res) => { const { ID_SECTION, NAME_TOPIC, DESCRIPTION_TOPIC } = req.body; if (!ID_SECTION) { return response(400, null, "Section ID is required", res); } if (!NAME_TOPIC) { return response(400, null, "Topic name is required", res); } if (!DESCRIPTION_TOPIC) { return response(400, null, "Topic description is required", res); } const transaction = await models.sequelize.transaction(); try { const section = await models.Section.findByPk(ID_SECTION); if (!section) { await transaction.rollback(); return response(404, null, "Section not found", res); } const newTopic = await models.Topic.create( { ID_SECTION, NAME_TOPIC, DESCRIPTION_TOPIC, }, { transaction } ); req.body.ID_TOPIC = newTopic.ID_TOPIC; await autoGenerateLevel(req, res, async () => { const levels = res.locals.createdLevels || []; const payload = { topic: newTopic, levels, }; await transaction.commit(); response( 201, payload, "Topic and related Level created successfully", res ); }); } catch (error) { await transaction.rollback(); console.log(error); response(500, null, "Internal Server Error", res); } }; export const updateTopicById = async (req, res) => { const { id } = req.params; const { ID_SECTION, NAME_TOPIC, DESCRIPTION_TOPIC } = req.body; try { const topic = await models.Topic.findByPk(id); if (!topic) { return response(404, null, "Topic not found", res); } if (ID_SECTION) { const section = await models.Section.findByPk(ID_SECTION); if (!section) { return response(404, null, "Section not found", res); } topic.ID_SECTION = ID_SECTION; } if (NAME_TOPIC) { topic.NAME_TOPIC = NAME_TOPIC; } if (DESCRIPTION_TOPIC) { topic.DESCRIPTION_TOPIC = DESCRIPTION_TOPIC; } await topic.save(); response(200, topic, "Topic updated successfully", res); } catch (error) { console.log(error); response(500, null, "Internal Server Error", res); } }; export const deleteTopicById = async (req, res) => { const { id } = req.params; try { const topic = await models.Topic.findByPk(id); if (!topic) { return response(404, null, "Topic not found", res); } topic.IS_DELETED = 1; await topic.save(); await models.Level.update({ IS_DELETED: 1 }, { where: { ID_TOPIC: id } }); await models.Exercise.update( { IS_DELETED: 1 }, { where: { ID_LEVEL: { [models.Op.in]: models.Sequelize.literal( `(SELECT ID_LEVEL FROM level WHERE ID_TOPIC = '${id}')` ), }, }, } ); response( 200, null, "Topic, levels, and related exercises soft deleted successfully", res ); } catch (error) { console.log(error); response(500, null, "Internal Server Error", res); } }; export const getCompletedTopicsBySection = async (req, res) => { try { const user = req.user; const userId = user.ID; // Ambil semua pembelajaran user const userLearnings = await models.StdLearning.findAll({ where: { ID: userId }, include: [ { model: models.Level, as: "level", include: [ { model: models.Topic, as: "levelTopic", attributes: ["ID_TOPIC", "NAME_TOPIC", "DESCRIPTION_TOPIC"], include: [ { model: models.Section, as: "topicSection", attributes: [ "ID_SECTION", "NAME_SECTION", "DESCRIPTION_SECTION", "THUMBNAIL", ], }, ], }, ], }, ], }); if (!userLearnings.length) { return response(200, null, "No topics found for this user", res); } const completedSections = {}; for (const learning of userLearnings) { const { level } = learning; 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, NAME_SECTION: section.NAME_SECTION, DESCRIPTION_SECTION: section.DESCRIPTION_SECTION, THUMBNAIL: section.THUMBNAIL, TOTAL_TOPICS: totalTopicsInSection, COMPLETED_TOPICS: 0, // Awal dengan 0 }; } // Cek apakah level saat ini adalah Level 6 untuk topik tersebut const level6 = await models.Level.findOne({ where: { NAME_LEVEL: "Level 6", ID_TOPIC: topic.ID_TOPIC, }, }); // Jika Level 6 ditemukan dan IS_PASS bernilai 1, tambahkan COMPLETED_TOPICS 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 return section; }); response(200, result, "Completed topics by section fetched successfully", res); } catch (error) { console.error(error); response(500, null, "Internal Server Error", res); } };