Files
starr-philproperties/controllers/admin/courses.controller.js
T
2026-05-16 11:04:36 +08:00

442 lines
14 KiB
JavaScript

const { Op } = require("sequelize");
const sequelize = require("../../config/db.config");
const R = require("../../utils/response.util");
const { paginate } = require("../../utils/paginate.util");
const Course = require("../../models/courses/courses.mdl");
const Unit = require("../../models/courses/units.mdl");
const Lesson = require("../../models/courses/lessons.mdl");
const LessonPage = require("../../models/courses/lesson-page.mdl");
const mdl_Users = require("../../models/users/users.mdl");
const notDeleted = { deletedAt: null };
// ─── Helpers ──────────────────────────────────────────────────────────────────
const auditByFields = ["createdBy", "updatedBy", "deletedBy"];
const adminExclude = [];
// ─── COURSE ───────────────────────────────────────────────────────────────────
exports.getCourses = async (req, res) => {
try {
const result = await paginate(Course, req, {
excludeAttributes: adminExclude,
auditOptions: { mdl_Users, parentAlias: "Course" },
context: "list",
findOptions: { where: { ...notDeleted } },
});
return R.success(res, "Courses retrieved.", result);
} catch (err) {
console.error("[COURSE][GET ALL]", err);
return R.error(res, "Could not retrieve courses.", 500);
}
};
exports.getCourse = async (req, res) => {
try {
const { courseId } = req.params;
const course = await Course.findOne({
where: { course_id: courseId, ...notDeleted },
include: [
{
model: Unit,
as: "units",
where: notDeleted,
required: false,
order_index: [["order", "ASC"]],
},
],
order_index: [[{ model: Unit, as: "units" }, "order_index", "ASC"]],
});
if (!course) return R.error(res, "Course not found.", 404);
return R.success(res, "Course retrieved.", { data: course });
} catch (err) {
console.error("[COURSE][GET ONE]", err);
return R.error(res, "Could not retrieve course.", 500);
}
};
exports.createCourse = async (req, res) => {
try {
const { title, description, order_index } = req.body;
if (!title) return R.error(res, "Title is required.", 400);
const course = await Course.create({
title,
description: description ?? null,
order_index: order_index ?? 0,
createdBy: req.body.createdBy ?? null,
});
return R.success(res, "Course created.", { data: course }, 201);
} catch (err) {
console.error("[COURSE][CREATE]", err);
return R.error(res, "Could not create course.", 500);
}
};
exports.updateCourse = 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 { title, description, order_index } = req.body;
if (title !== undefined) course.title = title;
if (description !== undefined) course.description = description;
if (order_index !== undefined) course.order_index = order_index;
course.updatedBy = req.body.updatedBy ?? null;
await course.save();
return R.success(res, "Course updated.", { data: course });
} catch (err) {
console.error("[COURSE][UPDATE]", err);
return R.error(res, "Could not update course.", 500);
}
};
exports.deleteCourse = 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);
await course.update({ deletedBy: req.body.deletedBy ?? null });
await course.destroy();
return R.success(res, "Course deleted.");
} catch (err) {
console.error("[COURSE][DELETE]", err);
return R.error(res, "Could not delete course.", 500);
}
};
// ─── 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, {
excludeAttributes: adminExclude,
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,
order: [["order_index", "ASC"]],
},
],
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 });
} 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_index } = 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_index ?? 0,
createdBy: req.body.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_index } = req.body;
if (title !== undefined) unit.title = title;
if (description !== undefined) unit.description = description;
if (order !== undefined) unit.order = order;
unit.updatedBy = req.body.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) => {
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);
await unit.update({ deletedBy: req.body.deletedBy ?? null });
await unit.destroy();
return R.success(res, "Unit deleted.");
} catch (err) {
console.error("[UNIT][DELETE]", err);
return R.error(res, "Could not delete unit.", 500);
}
};
// ─── LESSON ───────────────────────────────────────────────────────────────────
exports.getLessons = 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 result = await paginate(Lesson, req, {
excludeAttributes: adminExclude,
auditOptions: { mdl_Users, parentAlias: "Lesson" },
context: "list",
findOptions: {
where: { unit_id: unitId, ...notDeleted },
order: [["order_index", "ASC"]],
},
});
return R.success(res, "Lessons retrieved.", result);
} catch (err) {
console.error("[LESSON][GET ALL]", err);
return R.error(res, "Could not retrieve lessons.", 500);
}
};
exports.getLesson = async (req, res) => {
try {
const { courseId, unitId, lessonId } = req.params;
const lesson = await Lesson.findOne({
where: { lesson_id: lessonId, unit_id: unitId, ...notDeleted },
include: [
{
model: LessonPage,
as: "page",
required: false,
},
{
model: Unit,
as: "unit",
where: { course_id: courseId, ...notDeleted },
},
],
});
if (!lesson) return R.error(res, "Lesson not found.", 404);
return R.success(res, "Lesson retrieved.", { data: lesson });
} catch (err) {
console.error("[LESSON][GET ONE]", err);
return R.error(res, "Could not retrieve lesson.", 500);
}
};
exports.createLesson = async (req, res) => {
try {
const { courseId, unitId } = req.params;
const { title, description, order_index } = req.body;
if (!title) return R.error(res, "Title is required.", 400);
const unit = await Unit.findOne({
where: { unit_id: unitId, course_id: courseId, ...notDeleted },
});
if (!unit) return R.error(res, "Unit not found.", 404);
const lesson = await Lesson.create({
unit_id: unitId,
title,
description: description ?? null,
order_index: order_index ?? 0,
createdBy: req.body.createdBy ?? null,
});
// ── Auto-create an empty page for this lesson ─────────────────────────────
await LessonPage.create({
lesson_id: lesson.lesson_id,
blocks: [],
createdBy: req.body.createdBy ?? null,
});
return R.success(res, "Lesson created.", { data: lesson }, 201);
} catch (err) {
console.error("[LESSON][CREATE]", err);
return R.error(res, "Could not create lesson.", 500);
}
};
exports.updateLesson = async (req, res) => {
try {
const { courseId, unitId, lessonId } = req.params;
const lesson = await Lesson.findOne({
where: {
lesson_id: lessonId,
unit_id: unitId,
...notDeleted,
},
include: [{
model: Unit,
as: "unit",
where: { course_id: courseId, ...notDeleted },
}],
});
if (!lesson) return R.error(res, "Lesson not found.", 404);
const { title, description, order_index } = req.body;
if (title !== undefined) lesson.title = title;
if (description !== undefined) lesson.description = description;
if (order_index !== undefined) lesson.order_index = order_index;
lesson.updatedBy = req.body.updatedBy ?? null;
await lesson.save();
return R.success(res, "Lesson updated.", { data: lesson });
} catch (err) {
console.error("[LESSON][UPDATE]", err);
return R.error(res, "Could not update lesson.", 500);
}
};
exports.deleteLesson = async (req, res) => {
try {
const { courseId, unitId, lessonId } = req.params;
const lesson = await Lesson.findOne({
where: { lesson_id: lessonId, unit_id: unitId, ...notDeleted },
include: [{
model: Unit,
as: "unit",
where: { course_id: courseId, ...notDeleted },
}],
});
if (!lesson) return R.error(res, "Lesson not found.", 404);
await lesson.update({ deletedBy: req.body.deletedBy ?? null });
await lesson.destroy();
return R.success(res, "Lesson deleted.");
} catch (err) {
console.error("[LESSON][DELETE]", err);
return R.error(res, "Could not delete lesson.", 500);
}
};
// ─── LESSON PAGE ──────────────────────────────────────────────────────────────
exports.getLessonPage = async (req, res) => {
try {
const { lessonId } = req.params;
const page = await LessonPage.findOne({
where: { lesson_id: lessonId },
});
if (!page) return R.error(res, "Lesson page not found.", 404);
return R.success(res, "Lesson page retrieved.", { data: page });
} catch (err) {
console.error("[LESSON PAGE][GET]", err);
return R.error(res, "Could not retrieve lesson page.", 500);
}
};
exports.upsertLessonPage = async (req, res) => {
try {
const { lessonId } = req.params;
const { blocks } = req.body;
if (!Array.isArray(blocks)) return R.error(res, "blocks must be an array.", 400);
const lesson = await Lesson.findOne({ where: { lesson_id: lessonId, ...notDeleted } });
if (!lesson) return R.error(res, "Lesson not found.", 404);
const [page, created] = await LessonPage.upsert({
lesson_id: lessonId,
blocks,
updatedBy: req.body.updatedBy ?? null,
createdBy: req.body.updatedBy ?? null,
}, {
returning: true,
});
return R.success(
res,
created ? "Lesson page created." : "Lesson page updated.",
{ data: page },
created ? 201 : 200,
);
} catch (err) {
console.error("[LESSON PAGE][UPSERT]", err);
return R.error(res, "Could not save lesson page.", 500);
}
};