feat: add password change, class unenroll, and optional notify flags
Let admins skip SMS on user, class, and invoice actions, and let users change their own password with the current one.
This commit is contained in:
@@ -8,6 +8,7 @@ const AppError = require('../../utils/AppError');
|
||||
const { calculateMeta, escapeRegex, getSearchTerm } = require('../../utils/pagination');
|
||||
const { sendClassRegisteredSms } = require('../../utils/senders/smsMessages');
|
||||
const { buildClassScheduleContext } = require('../../utils/classSchedule');
|
||||
const { pickNotifyFlags } = require('../../utils/notifyFlags');
|
||||
const logger = require('../../utils/logger');
|
||||
|
||||
const getAll = async (query) => {
|
||||
@@ -64,10 +65,11 @@ const remove = async (id) => {
|
||||
if (!cls) throw new AppError('CLASS_NOT_FOUND');
|
||||
};
|
||||
|
||||
const registerUsers = async (classId, userIds) => {
|
||||
const registerUsers = async (classId, userIds, notifyInput = {}) => {
|
||||
const cls = await Class.findById(classId).populate({ path: 'course', select: 'title' });
|
||||
if (!cls) throw new AppError('CLASS_NOT_FOUND');
|
||||
|
||||
const notify = pickNotifyFlags(notifyInput);
|
||||
const toAdd = (userIds || []).filter(
|
||||
(id) => !cls.students.map((s) => s.toString()).includes(id.toString())
|
||||
);
|
||||
@@ -78,26 +80,41 @@ const registerUsers = async (classId, userIds) => {
|
||||
cls.students.push(...toAdd);
|
||||
await cls.save();
|
||||
|
||||
const classLabel = cls.name || cls.course?.title || 'کلاس';
|
||||
const sessions = await Session.find({ class: classId })
|
||||
.select('day startTime endTime')
|
||||
.sort({ day: 1 })
|
||||
.lean();
|
||||
const schedule = buildClassScheduleContext(cls, sessions);
|
||||
const users = await User.find({ _id: { $in: toAdd } }).select('phoneNumber').lean();
|
||||
await Promise.all(
|
||||
users.map(async (user) => {
|
||||
if (!user.phoneNumber) return;
|
||||
try {
|
||||
await sendClassRegisteredSms(user.phoneNumber, classLabel, user._id, {
|
||||
courseName: cls.course?.title || classLabel,
|
||||
...schedule
|
||||
});
|
||||
} catch (err) {
|
||||
logger.error(`[registerUsers] SMS failed for ${user.phoneNumber}: ${err.message}`);
|
||||
}
|
||||
})
|
||||
);
|
||||
if (notify.sms) {
|
||||
const classLabel = cls.name || cls.course?.title || 'کلاس';
|
||||
const sessions = await Session.find({ class: classId })
|
||||
.select('day startTime endTime')
|
||||
.sort({ day: 1 })
|
||||
.lean();
|
||||
const schedule = buildClassScheduleContext(cls, sessions);
|
||||
const users = await User.find({ _id: { $in: toAdd } }).select('phoneNumber').lean();
|
||||
await Promise.all(
|
||||
users.map(async (user) => {
|
||||
if (!user.phoneNumber) return;
|
||||
try {
|
||||
await sendClassRegisteredSms(user.phoneNumber, classLabel, user._id, {
|
||||
courseName: cls.course?.title || classLabel,
|
||||
...schedule
|
||||
});
|
||||
} catch (err) {
|
||||
logger.error(`[registerUsers] SMS failed for ${user.phoneNumber}: ${err.message}`);
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
return getOne(classId);
|
||||
};
|
||||
|
||||
const removeUser = async (classId, userId) => {
|
||||
const cls = await Class.findById(classId);
|
||||
if (!cls) throw new AppError('CLASS_NOT_FOUND');
|
||||
|
||||
const before = cls.students.length;
|
||||
cls.students = cls.students.filter((id) => id.toString() !== String(userId));
|
||||
if (cls.students.length !== before) {
|
||||
await cls.save();
|
||||
}
|
||||
|
||||
return getOne(classId);
|
||||
};
|
||||
@@ -121,4 +138,4 @@ const getMyClasses = async (userId, query = {}) => {
|
||||
return { data: items, meta: calculateMeta(total, page, limit) };
|
||||
};
|
||||
|
||||
module.exports = { getAll, getOne, create, update, remove, registerUsers, getMyClasses };
|
||||
module.exports = { getAll, getOne, create, update, remove, registerUsers, removeUser, getMyClasses };
|
||||
|
||||
Reference in New Issue
Block a user