mirror of
https://github.com/rgrgogu/new_starr.git
synced 2026-09-27 00:12:54 +08:00
units,lesson as standalone
This commit is contained in:
@@ -26,6 +26,7 @@ const { restoreOne, restoreMany } = require("../../utils/courses/restore.util");
|
||||
const { permanentDeleteOne, permanentDeleteMany } = require("../../utils/courses/permanentDelete.util");
|
||||
const { TaskCompletion } = require('../../models/task/task_completion.mdl');
|
||||
const logActivity = require('../../utils/logActivity.util');
|
||||
const { nextOrderIndex, reorderJunction } = require('../../utils/courses/hierarchy.util');
|
||||
|
||||
// ─── URL normalizer — ensures protocol prefix so link_url is never a relative path ──
|
||||
const normalizeUrl = (url) => {
|
||||
@@ -36,7 +37,7 @@ const normalizeUrl = (url) => {
|
||||
|
||||
// ─── Allowed filter/sort fields ───────────────────────────────────────────────
|
||||
const TASK_LIST_FIELDS = ['name', 'description', 'createdAt', 'updatedAt', 'deletedAt'];
|
||||
const TASK_FIELDS = ['name', 'description', 'deadline', 'status', 'createdAt', 'updatedAt', 'deletedAt'];
|
||||
const TASK_FIELDS = ['name', 'description', 'deadline', 'order_index', 'is_required', 'status', 'createdAt', 'updatedAt', 'deletedAt'];
|
||||
const FILTERABLE_MODELS = {
|
||||
TaskList: TaskList,
|
||||
Task: Task,
|
||||
@@ -420,6 +421,38 @@ exports.assignGroups = async (req, res) => {
|
||||
|
||||
await t.commit();
|
||||
logActivity(req.user.user_id, 'assign_groups', { entityType: 'task_list', entityId: Number(taskListId), details: { group_ids: newIds } });
|
||||
|
||||
// ── Notify every member of the newly-assigned group(s) ─────────────────
|
||||
if (newIds.length) {
|
||||
try {
|
||||
const taskCount = await Task.count({ where: { task_list_id: taskListId } });
|
||||
const memberRows = await mdl_UserGroupMembers.findAll({
|
||||
where: { group_id: newIds },
|
||||
attributes: ['user_id'],
|
||||
});
|
||||
const seenUsers = new Set();
|
||||
const userIds = memberRows.filter(({ user_id }) => {
|
||||
if (seenUsers.has(user_id)) return false;
|
||||
seenUsers.add(user_id);
|
||||
return true;
|
||||
}).map((m) => m.user_id);
|
||||
|
||||
if (userIds.length) {
|
||||
const now = new Date();
|
||||
const notify = await renderNotification({ type: 'task_assigned', data: {
|
||||
taskListName: taskList.name,
|
||||
taskCount,
|
||||
} });
|
||||
await UserNotification.bulkCreate(
|
||||
userIds.map((user_id) => ({ user_id, ...notify, seen: false, createdAt: now, updatedAt: now })),
|
||||
{ validate: false }
|
||||
);
|
||||
}
|
||||
} catch (notifyErr) {
|
||||
console.error('[ADMIN][ASSIGN GROUPS][NOTIFY]', notifyErr);
|
||||
}
|
||||
}
|
||||
|
||||
return R.success(res, `${newIds.length} group(s) assigned.`, {
|
||||
assigned_ids: newIds,
|
||||
already_assigned_ids: existingIds,
|
||||
@@ -556,7 +589,7 @@ exports.createTask = async (req, res) => {
|
||||
const t = await sequelize.transaction();
|
||||
try {
|
||||
const { taskListId } = req.params;
|
||||
const { name, description, deadline, requirements = [] } = req.body;
|
||||
const { name, description, deadline, is_required, requirements = [] } = req.body;
|
||||
|
||||
if (!name) return R.error(res, 'Task name is required.', 400);
|
||||
|
||||
@@ -566,12 +599,16 @@ exports.createTask = async (req, res) => {
|
||||
return R.error(res, 'Task list not found.', 404);
|
||||
}
|
||||
|
||||
const order_index = await nextOrderIndex(Task, { task_list_id: taskListId }, t);
|
||||
|
||||
const task = await Task.create(
|
||||
{
|
||||
task_list_id: taskListId,
|
||||
name,
|
||||
description,
|
||||
deadline: deadline || null,
|
||||
order_index,
|
||||
is_required: is_required ?? true,
|
||||
createdBy: req.user.user_id,
|
||||
updatedBy: req.user.user_id,
|
||||
},
|
||||
@@ -630,10 +667,10 @@ exports.updateTask = async (req, res) => {
|
||||
return R.error(res, 'Task not found.', 404);
|
||||
}
|
||||
|
||||
const { name, description, deadline, status, requirements } = req.body;
|
||||
const { name, description, deadline, status, is_required, requirements } = req.body;
|
||||
|
||||
await task.update(
|
||||
{ name, description, deadline: deadline || null, status, updatedBy: req.user.user_id },
|
||||
{ name, description, deadline: deadline || null, status, is_required, updatedBy: req.user.user_id },
|
||||
{ transaction: t }
|
||||
);
|
||||
|
||||
@@ -757,6 +794,31 @@ exports.updateTask = async (req, res) => {
|
||||
}
|
||||
};
|
||||
|
||||
// ─── REORDER ──────────────────────────────────────────────────────────────────
|
||||
// PATCH /admin/task-lists/:taskListId/tasks/order { task_ids: [orderedIds] }
|
||||
|
||||
exports.reorderTasks = async (req, res) => {
|
||||
const t = await sequelize.transaction();
|
||||
try {
|
||||
const { taskListId } = req.params;
|
||||
const { task_ids = [] } = req.body;
|
||||
if (!task_ids.length) { await t.rollback(); return R.error(res, 'task_ids is required.', 400); }
|
||||
|
||||
const taskList = await TaskList.findByPk(taskListId, { transaction: t });
|
||||
if (!taskList) { await t.rollback(); return R.error(res, 'Task list not found.', 404); }
|
||||
|
||||
await reorderJunction(Task, 'task_list_id', taskListId, 'task_id', task_ids, t);
|
||||
await t.commit();
|
||||
|
||||
logActivity(req.user.user_id, 'reorder_tasks', { entityType: 'task_list', entityId: taskListId, details: { task_ids } });
|
||||
return R.success(res, 'Task order updated.');
|
||||
} catch (err) {
|
||||
await t.rollback();
|
||||
console.error('[ADMIN][REORDER TASKS]', err);
|
||||
return R.error(res, 'Could not reorder tasks.', 500);
|
||||
}
|
||||
};
|
||||
|
||||
// ─── GET ARCHIVED TASK LISTS ──────────────────────────────────────────────────
|
||||
|
||||
exports.getArchivedTaskLists = async (req, res) => {
|
||||
|
||||
Reference in New Issue
Block a user