mirror of
https://github.com/rgrgogu/new_starr.git
synced 2026-09-27 00:12:54 +08:00
token issues more for pdf preview missing
With DEV to QAS live test environment Signed-off-by: Kenneth Obsequio <k80308392@gmail.com>
This commit is contained in:
@@ -12,7 +12,7 @@ const { syncJunction } = require("../../utils/courses/junction.util");
|
||||
const { archiveOne, archiveMany } = require("../../utils/courses/archive.util");
|
||||
const { restoreOne, restoreMany } = require("../../utils/courses/restore.util");
|
||||
const { permanentDeleteOne, permanentDeleteMany } = require("../../utils/courses/permanentDelete.util");
|
||||
const { flattenUnits, flattenLessons, nextOrderIndex, reorderJunction } = require("../../utils/courses/hierarchy.util");
|
||||
const { flattenUnits, flattenLessons, nextOrderIndex, reorderJunction, getCourseUnitIds, countCourseLessons } = require("../../utils/courses/hierarchy.util");
|
||||
const { getFieldValues } = require("../../utils/fieldValues.util");
|
||||
const logActivity = require('../../utils/logActivity.util');
|
||||
const UserNotification = require('../../models/notifications/user_notification.mdl');
|
||||
@@ -2393,6 +2393,36 @@ exports.getCoursesFlat = async (req, res) => {
|
||||
}
|
||||
};
|
||||
|
||||
// One course's structure counts, looked up by uuid — used by the Task
|
||||
// requirement viewer (ViewTask.jsx) to show how many units/lessons/quizzes a
|
||||
// "Read a Course" requirement actually points to, without pulling the full
|
||||
// nested course tree.
|
||||
exports.getCourseStructureCounts = async (req, res) => {
|
||||
try {
|
||||
const { uuid } = req.params;
|
||||
|
||||
const course = await Course.findOne({ where: { uuid, ...notDeleted }, attributes: ["course_id"] });
|
||||
if (!course) return R.error(res, "Course not found.", 404);
|
||||
|
||||
const unitIds = await getCourseUnitIds(course.course_id);
|
||||
const [lessonCount, quizCount] = await Promise.all([
|
||||
countCourseLessons(course.course_id),
|
||||
unitIds.length
|
||||
? UnitQuiz.count({ where: { unit_id: { [Op.in]: unitIds } } })
|
||||
: 0,
|
||||
]);
|
||||
|
||||
return R.success(res, "Course structure counts retrieved.", {
|
||||
unitCount: unitIds.length,
|
||||
lessonCount,
|
||||
quizCount,
|
||||
});
|
||||
} catch (err) {
|
||||
console.error("[COURSE][STRUCTURE COUNTS]", err);
|
||||
return R.error(res, "Could not retrieve course structure counts.", 500);
|
||||
}
|
||||
};
|
||||
|
||||
exports.getCoursesBySubscription = async (req, res) => {
|
||||
try {
|
||||
const { slug } = req.query;
|
||||
|
||||
@@ -558,8 +558,8 @@ exports.unassignGroups = async (req, res) => {
|
||||
// Body: { group_ids: [1, 2, 3] }
|
||||
//
|
||||
// Replaces the full assigned-group set in one request/one transaction, so an
|
||||
// edit that both adds and removes groups only costs a single sensitiveOpsLimiter
|
||||
// hit instead of two (assign + unassign).
|
||||
// edit that both adds and removes groups is a single atomic operation instead
|
||||
// of two separate calls (assign + unassign).
|
||||
|
||||
exports.syncGroups = async (req, res) => {
|
||||
const t = await sequelize.transaction();
|
||||
@@ -1056,6 +1056,102 @@ exports.createTask = async (req, res) => {
|
||||
}
|
||||
};
|
||||
|
||||
// ─── BULK CREATE ──────────────────────────────────────────────────────────────
|
||||
// POST /:taskListId/tasks/bulk
|
||||
// Body: { tasks: [{ name, description, deadline, requirements }, ...] }
|
||||
//
|
||||
// Batches Task + TaskRequirement creation into one transaction/request — used
|
||||
// by the Create Task List wizard so queuing N tasks costs one request instead
|
||||
// of N sequential POST .../tasks calls (see CreateTaskList.jsx handleCreate).
|
||||
// Prerequisite wiring is intentionally not supported here: queued tasks can't
|
||||
// reference a not-yet-created sibling task's id, same as createTask today
|
||||
// when called from this flow.
|
||||
exports.createTasksBulk = async (req, res) => {
|
||||
const t = await sequelize.transaction();
|
||||
try {
|
||||
const { taskListId } = req.params;
|
||||
const { tasks } = req.body;
|
||||
|
||||
if (!Array.isArray(tasks) || !tasks.length)
|
||||
return R.error(res, 'No tasks provided.', 400);
|
||||
if (tasks.some((task) => !task.name)) {
|
||||
return R.error(res, 'Every task requires a name.', 400);
|
||||
}
|
||||
|
||||
const taskList = await TaskList.findByPk(taskListId, { transaction: t });
|
||||
if (!taskList) {
|
||||
await t.rollback();
|
||||
return R.error(res, 'Task list not found.', 404);
|
||||
}
|
||||
|
||||
const startOrderIndex = await nextOrderIndex(Task, { task_list_id: taskListId }, t);
|
||||
|
||||
const createdTasks = await Task.bulkCreate(
|
||||
tasks.map((task, i) => ({
|
||||
task_list_id: taskListId,
|
||||
name: task.name,
|
||||
description: task.description,
|
||||
deadline: task.deadline || null,
|
||||
order_index: startOrderIndex + i,
|
||||
createdBy: req.user.user_id,
|
||||
updatedBy: req.user.user_id,
|
||||
})),
|
||||
{ transaction: t }
|
||||
);
|
||||
|
||||
const reqRows = createdTasks.flatMap((task, i) =>
|
||||
(tasks[i].requirements ?? []).map((r, j) => ({
|
||||
...r,
|
||||
task_id: task.task_id,
|
||||
order: r.order ?? j,
|
||||
reference_id: r.reference_id || null, // '' → null (UUID column)
|
||||
reference_label: r.reference_label || null, // '' → null
|
||||
link_url: normalizeUrl(r.link_url),
|
||||
link_label: r.link_label || null,
|
||||
createdBy: req.user.user_id,
|
||||
updatedBy: req.user.user_id,
|
||||
}))
|
||||
);
|
||||
if (reqRows.length) await TaskRequirement.bulkCreate(reqRows, { transaction: t });
|
||||
|
||||
await t.commit();
|
||||
|
||||
const taskIds = createdTasks.map((task) => task.task_id);
|
||||
const full = await Task.findAll({
|
||||
where: { task_id: { [Op.in]: taskIds } },
|
||||
attributes: { exclude: adminExclude },
|
||||
include: [{
|
||||
model: TaskRequirement,
|
||||
as: 'requirements',
|
||||
attributes: { exclude: adminExclude },
|
||||
order: [['order', 'ASC']],
|
||||
}, PREREQUISITE_INCLUDE],
|
||||
order: [['order_index', 'ASC']],
|
||||
});
|
||||
|
||||
// Heads-up only — never blocks creation. See getPreCompletedAssignees.
|
||||
const allRequirements = full.flatMap((task) =>
|
||||
(task.requirements ?? []).map((r) => ({ type: r.type, reference_id: r.reference_id, reference_label: r.reference_label }))
|
||||
);
|
||||
const warnings = await getPreCompletedAssignees(taskListId, allRequirements);
|
||||
|
||||
logActivity(req.user.user_id, 'bulk_create_tasks', {
|
||||
entityType: 'task_list',
|
||||
entityId: Number(taskListId),
|
||||
details: { count: createdTasks.length, task_ids: taskIds },
|
||||
});
|
||||
|
||||
return R.success(res, `${createdTasks.length} task(s) created successfully.`, {
|
||||
tasks: full.map((task) => task.toJSON()),
|
||||
warnings,
|
||||
}, 201);
|
||||
} catch (err) {
|
||||
await t.rollback();
|
||||
console.error('[ADMIN][BULK CREATE TASKS]', err);
|
||||
return R.error(res, 'Could not create tasks.', 500);
|
||||
}
|
||||
};
|
||||
|
||||
// ─── UPDATE ───────────────────────────────────────────────────────────────────
|
||||
|
||||
exports.updateTask = async (req, res) => {
|
||||
|
||||
Reference in New Issue
Block a user