mirror of
https://github.com/rgrgogu/new_starr.git
synced 2026-09-27 00:12:54 +08:00
Adjusted
This commit is contained in:
@@ -0,0 +1,335 @@
|
||||
"use strict";
|
||||
|
||||
const { Op } = require("sequelize");
|
||||
const sequelize = require("../../config/db.config");
|
||||
const R = require("../../utils/response.util");
|
||||
const { paginate } = require("../../utils/paginate.util");
|
||||
const { archiveOne, archiveMany } = require("../../utils/courses/archive.util");
|
||||
const { restoreOne, restoreMany } = require("../../utils/courses/restore.util");
|
||||
|
||||
// ── Models ────────────────────────────────────────────────────────────────────
|
||||
|
||||
const {
|
||||
Course, Unit, Lesson,
|
||||
UnitQuiz, QuizQuestion, QuizOption,
|
||||
} = require("../../models/courses/courses.associations");
|
||||
|
||||
const mdl_Users = require("../../models/users/users.mdl");
|
||||
|
||||
const notDeleted = { deletedAt: null };
|
||||
const onlyDeleted = { deletedAt: { [Op.not]: null } };
|
||||
|
||||
// ══════════════════════════════════════════════════════════════════════════════
|
||||
// UNIT
|
||||
// ══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
exports.getUnits = async (req, res) => {
|
||||
try {
|
||||
const { courseId } = req.params;
|
||||
|
||||
const course = await Course.findOne({ where: { course_id: courseId, ...notDeleted } });
|
||||
if (!course) return R.error(res, "Course not found.", 404);
|
||||
|
||||
const result = await paginate(Unit, req, {
|
||||
auditOptions: { mdl_Users, parentAlias: "Unit" },
|
||||
context: "list",
|
||||
findOptions: {
|
||||
where: { course_id: courseId, ...notDeleted },
|
||||
order: [["order_index", "ASC"]],
|
||||
},
|
||||
});
|
||||
|
||||
return R.success(res, "Units retrieved.", result);
|
||||
} catch (err) {
|
||||
console.error("[UNIT][GET ALL]", err);
|
||||
return R.error(res, "Could not retrieve units.", 500);
|
||||
}
|
||||
};
|
||||
|
||||
exports.getUnit = async (req, res) => {
|
||||
try {
|
||||
const { courseId, unitId } = req.params;
|
||||
|
||||
const unit = await Unit.findOne({
|
||||
where: { unit_id: unitId, course_id: courseId, ...notDeleted },
|
||||
include: [
|
||||
{ model: Lesson, as: "lessons", where: notDeleted, required: false },
|
||||
{ model: UnitQuiz, as: "quiz", required: false },
|
||||
],
|
||||
order: [[{ model: Lesson, as: "lessons" }, "order_index", "ASC"]],
|
||||
});
|
||||
|
||||
if (!unit) return R.error(res, "Unit not found.", 404);
|
||||
return R.success(res, "Unit retrieved.", { data: unit.toJSON() });
|
||||
} catch (err) {
|
||||
console.error("[UNIT][GET ONE]", err);
|
||||
return R.error(res, "Could not retrieve unit.", 500);
|
||||
}
|
||||
};
|
||||
|
||||
exports.createUnit = async (req, res) => {
|
||||
try {
|
||||
const { courseId } = req.params;
|
||||
const { title, description, order, createdBy } = req.body;
|
||||
|
||||
if (!title) return R.error(res, "Title is required.", 400);
|
||||
|
||||
const course = await Course.findOne({ where: { course_id: courseId, ...notDeleted } });
|
||||
if (!course) return R.error(res, "Course not found.", 404);
|
||||
|
||||
const unit = await Unit.create({
|
||||
course_id: courseId,
|
||||
title,
|
||||
description: description ?? null,
|
||||
order_index: order ?? 0,
|
||||
duration_seconds: 0,
|
||||
createdBy: createdBy ?? null,
|
||||
});
|
||||
|
||||
return R.success(res, "Unit created.", { data: unit }, 201);
|
||||
} catch (err) {
|
||||
console.error("[UNIT][CREATE]", err);
|
||||
return R.error(res, "Could not create unit.", 500);
|
||||
}
|
||||
};
|
||||
|
||||
exports.updateUnit = async (req, res) => {
|
||||
try {
|
||||
const { courseId, unitId } = req.params;
|
||||
|
||||
const unit = await Unit.findOne({ where: { unit_id: unitId, course_id: courseId, ...notDeleted } });
|
||||
if (!unit) return R.error(res, "Unit not found.", 404);
|
||||
|
||||
const { title, description, order, updatedBy } = req.body;
|
||||
|
||||
if (title !== undefined) unit.title = title;
|
||||
if (description !== undefined) unit.description = description;
|
||||
if (order !== undefined) unit.order_index = order;
|
||||
unit.updatedBy = updatedBy ?? null;
|
||||
|
||||
await unit.save();
|
||||
return R.success(res, "Unit updated.", { data: unit });
|
||||
} catch (err) {
|
||||
console.error("[UNIT][UPDATE]", err);
|
||||
return R.error(res, "Could not update unit.", 500);
|
||||
}
|
||||
};
|
||||
|
||||
exports.deleteUnit = async (req, res) => {
|
||||
const t = await sequelize.transaction();
|
||||
try {
|
||||
const { courseId, unitId } = req.params;
|
||||
const record = await archiveOne(
|
||||
Unit,
|
||||
{ unit_id: unitId, course_id: courseId, ...notDeleted },
|
||||
req.body.deletedBy,
|
||||
t
|
||||
);
|
||||
if (!record) return R.error(res, "Unit not found.", 404);
|
||||
await t.commit();
|
||||
return R.success(res, "Unit archived.");
|
||||
} catch (err) {
|
||||
await t.rollback();
|
||||
console.error("[UNIT][DELETE]", err);
|
||||
return R.error(res, "Could not archive unit.", 500);
|
||||
}
|
||||
};
|
||||
|
||||
exports.bulkArchiveUnits = async (req, res) => {
|
||||
const t = await sequelize.transaction();
|
||||
try {
|
||||
const { courseId } = req.params;
|
||||
const { ids = [], deletedBy } = req.body;
|
||||
if (!ids.length) return R.error(res, "No IDs provided.", 400);
|
||||
|
||||
const units = await Unit.findAll({
|
||||
where: { unit_id: ids, course_id: courseId, ...notDeleted },
|
||||
});
|
||||
const validIds = units.map((u) => u.unit_id);
|
||||
|
||||
const count = await archiveMany(Unit, "unit_id", validIds, deletedBy, t);
|
||||
await t.commit();
|
||||
return R.success(res, `${count} unit${count !== 1 ? "s" : ""} archived.`);
|
||||
} catch (err) {
|
||||
await t.rollback();
|
||||
console.error("[UNIT][BULK ARCHIVE]", err);
|
||||
return R.error(res, "Could not archive units.", 500);
|
||||
}
|
||||
};
|
||||
|
||||
exports.getArchivedUnits = async (req, res) => {
|
||||
try {
|
||||
const { courseId } = req.params;
|
||||
|
||||
const course = await Course.findOne({ where: { course_id: courseId }, paranoid: false });
|
||||
if (!course) return R.error(res, "Course not found.", 404);
|
||||
|
||||
const result = await paginate(Unit, req, {
|
||||
auditOptions: { mdl_Users, parentAlias: "Unit" },
|
||||
context: "list",
|
||||
findOptions: {
|
||||
where: { course_id: courseId, ...onlyDeleted },
|
||||
paranoid: false,
|
||||
order: [["order_index", "ASC"]],
|
||||
},
|
||||
});
|
||||
|
||||
return R.success(res, "Archived units retrieved.", result);
|
||||
} catch (err) {
|
||||
console.error("[UNIT][GET ARCHIVES]", err);
|
||||
return R.error(res, "Could not retrieve archived units.", 500);
|
||||
}
|
||||
};
|
||||
|
||||
exports.getArchivedUnit = async (req, res) => {
|
||||
try {
|
||||
const { courseId, unitId } = req.params;
|
||||
const unit = await Unit.findOne({
|
||||
where: { unit_id: unitId, course_id: courseId, ...onlyDeleted },
|
||||
paranoid: false,
|
||||
});
|
||||
if (!unit) return R.error(res, "Archived unit not found.", 404);
|
||||
return R.success(res, "Archived unit retrieved.", { data: unit.toJSON() });
|
||||
} catch (err) {
|
||||
console.error("[UNIT][GET ARCHIVE ONE]", err);
|
||||
return R.error(res, "Could not retrieve archived unit.", 500);
|
||||
}
|
||||
};
|
||||
|
||||
exports.restoreUnit = async (req, res) => {
|
||||
const t = await sequelize.transaction();
|
||||
try {
|
||||
const { courseId, unitId } = req.params;
|
||||
const record = await restoreOne(
|
||||
Unit,
|
||||
{ unit_id: unitId, course_id: courseId, ...onlyDeleted },
|
||||
req.body.restoredBy,
|
||||
t
|
||||
);
|
||||
if (!record) return R.error(res, "Archived unit not found.", 404);
|
||||
await t.commit();
|
||||
return R.success(res, "Unit restored.", { data: record });
|
||||
} catch (err) {
|
||||
await t.rollback();
|
||||
console.error("[UNIT][RESTORE]", err);
|
||||
return R.error(res, "Could not restore unit.", 500);
|
||||
}
|
||||
};
|
||||
|
||||
exports.bulkRestoreUnits = async (req, res) => {
|
||||
const t = await sequelize.transaction();
|
||||
try {
|
||||
const { courseId } = req.params;
|
||||
const { ids = [], restoredBy } = req.body;
|
||||
if (!ids.length) return R.error(res, "No IDs provided.", 400);
|
||||
|
||||
const units = await Unit.findAll({
|
||||
where: { unit_id: ids, course_id: courseId, ...onlyDeleted },
|
||||
paranoid: false,
|
||||
});
|
||||
const validIds = units.map((u) => u.unit_id);
|
||||
|
||||
const count = await restoreMany(Unit, "unit_id", validIds, restoredBy, t);
|
||||
await t.commit();
|
||||
return R.success(res, `${count} unit${count !== 1 ? "s" : ""} restored.`);
|
||||
} catch (err) {
|
||||
await t.rollback();
|
||||
console.error("[UNIT][BULK RESTORE]", err);
|
||||
return R.error(res, "Could not restore units.", 500);
|
||||
}
|
||||
};
|
||||
|
||||
// ══════════════════════════════════════════════════════════════════════════════
|
||||
// UNIT QUIZ
|
||||
// ══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
exports.getQuiz = async (req, res) => {
|
||||
try {
|
||||
const { courseId, unitId } = req.params;
|
||||
|
||||
const unit = await Unit.findOne({ where: { unit_id: unitId, course_id: courseId, ...notDeleted } });
|
||||
if (!unit) return R.error(res, "Unit not found.", 404);
|
||||
|
||||
const quiz = await UnitQuiz.findOne({
|
||||
where: { unit_id: unitId, ...notDeleted },
|
||||
include: [{
|
||||
model: QuizQuestion, as: "questions",
|
||||
where: notDeleted, required: false,
|
||||
include: [{ model: QuizOption, as: "options", order: [["order_index", "ASC"]] }],
|
||||
}],
|
||||
});
|
||||
|
||||
if (!quiz) return R.error(res, "Quiz not found.", 404);
|
||||
return R.success(res, "Quiz retrieved.", { data: quiz });
|
||||
} catch (err) {
|
||||
console.error("[QUIZ][GET]", err);
|
||||
return R.error(res, "Could not retrieve quiz.", 500);
|
||||
}
|
||||
};
|
||||
|
||||
exports.createQuiz = async (req, res) => {
|
||||
try {
|
||||
const { courseId, unitId } = req.params;
|
||||
const { title, is_required, passing_score, createdBy } = req.body;
|
||||
|
||||
const unit = await Unit.findOne({ where: { unit_id: unitId, course_id: courseId, ...notDeleted } });
|
||||
if (!unit) return R.error(res, "Unit not found.", 404);
|
||||
|
||||
const existing = await UnitQuiz.findOne({ where: { unit_id: unitId, ...notDeleted } });
|
||||
if (existing) return R.error(res, "Quiz already exists for this unit.", 409);
|
||||
|
||||
const quiz = await UnitQuiz.create({
|
||||
unit_id: unitId,
|
||||
title: title ?? null,
|
||||
is_required: is_required ?? false,
|
||||
passing_score: passing_score ?? 70,
|
||||
createdBy: createdBy ?? null,
|
||||
});
|
||||
|
||||
return R.success(res, "Quiz created.", { data: quiz }, 201);
|
||||
} catch (err) {
|
||||
console.error("[QUIZ][CREATE]", err);
|
||||
return R.error(res, "Could not create quiz.", 500);
|
||||
}
|
||||
};
|
||||
|
||||
exports.updateQuiz = async (req, res) => {
|
||||
try {
|
||||
const { courseId, unitId, quizId } = req.params;
|
||||
const { title, is_required, passing_score, updatedBy } = req.body;
|
||||
|
||||
const quiz = await UnitQuiz.findOne({ where: { quiz_id: quizId, unit_id: unitId, ...notDeleted } });
|
||||
if (!quiz) return R.error(res, "Quiz not found.", 404);
|
||||
|
||||
if (title !== undefined) quiz.title = title;
|
||||
if (is_required !== undefined) quiz.is_required = is_required;
|
||||
if (passing_score !== undefined) quiz.passing_score = passing_score;
|
||||
quiz.updatedBy = updatedBy ?? null;
|
||||
|
||||
await quiz.save();
|
||||
return R.success(res, "Quiz updated.", { data: quiz });
|
||||
} catch (err) {
|
||||
console.error("[QUIZ][UPDATE]", err);
|
||||
return R.error(res, "Could not update quiz.", 500);
|
||||
}
|
||||
};
|
||||
|
||||
exports.deleteQuiz = async (req, res) => {
|
||||
const t = await sequelize.transaction();
|
||||
try {
|
||||
const { unitId, quizId } = req.params;
|
||||
const record = await archiveOne(
|
||||
UnitQuiz,
|
||||
{ quiz_id: quizId, unit_id: unitId, ...notDeleted },
|
||||
req.body.deletedBy,
|
||||
t
|
||||
);
|
||||
if (!record) return R.error(res, "Quiz not found.", 404);
|
||||
await t.commit();
|
||||
return R.success(res, "Quiz archived.");
|
||||
} catch (err) {
|
||||
await t.rollback();
|
||||
console.error("[QUIZ][DELETE]", err);
|
||||
return R.error(res, "Could not archive quiz.", 500);
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user